Although almost every processor and programming language supports floating point operations, most programmers seldom pay attention to it. This is easy to understand-most of us seldom need non-integer types. In addition to scientific computing and occasional timing or benchmarking procedures, it is almost useless in other cases. Similarly, most developers can easily ignore arbitrary decimal numbers provided by java. math. BigDecimal-they are not used by most applications. However, in an integer-based program, it is sometimes unexpectedly necessary to represent non-integer data. For example, JDBC uses BigDecimal as the preferred interchange format for SQL DECIMAL columns.

[IEEE floating point]

Java supports two basic floating point types: float and double, and the packaging classes Float and Double corresponding to them. Both are based on the IEEE 754 standard, which defines binary standards for 32-bit floating point and 64-bit double-precision floating point binary decimals.

IEEE 754 uses a scientific notation to represent a floating point number with a base number of 2 decimal places. The IEEE floating point number uses a 1-digit symbol to represent the number, an 8-digit symbol to represent the index, and a 23-digit symbol to represent the ending number, that is, the fractional part. Indexes that are signed integers can be positive or negative. The decimal part is represented by a decimal number (base number 2), which means that the highest bit corresponds to the value? (2-1), the second digit corresponds? (2-2), and so on. For double-precision floating-point numbers, 11 bits are used to represent the index, and 52 bits are used to represent the ending number. The format of the IEEE floating point value is 1.

Because scientific notation can be used to represent a given number in multiple ways, it is necessary to normalize the floating point so that it can be expressed by a decimal point with a base number of 2 and a decimal point left of 1, adjust the index as needed to obtain the required number. Therefore, for example, the number 1.25 can be expressed as the ending number of 1.01, and the index is 0: (-1) 0*1.01 2*2 0

The number 10.0 can be expressed as the ending number of 1.01, and the index is 3: (-1) 0*1.01 2*2 3

[Special number]

Except for the standard range of values allowed by encoding (for float, from 1.4e-45 to 3.4028235e + 38 ), there are also some special values that represent infinity, negative infinity,-0, and NaN (which represent "not a number. The existence of these values is to use a number in the floating point value set to represent the results in the case of an error condition (such as Arithmetic overflow, square root of a negative number, divided by 0.

These special numbers have some unusual characteristics. For example, 0 and-0 are different values, but they are considered equal when they are compared. Divide the number by a non-zero number and the result is 0. The special number NaN is unordered. When the =, <, and> operator is used to compare NaN with other floating point values, the result is false. If f is NaN, false is returned even if (f = f. If you want to compare the floating point value with NaN, use the Float. isNaN () method. Table 1 shows the infinite and NaN attributes.

[Table 1. Attributes of special floating point values]

Expression result

Math. sqrt (-1.0)-> NaN

0.0/0.0-> NaN

1.0/0.0-> infinity

-1.0/0.0-> negative infinity

NaN + 1.0-> NaN

Infinity + 1.0-> infinity

Infinity + Infinity-> infinity

NaN> 1.0-> false

NaN = 1.0-> false

NaN <1.0-> false

NaN = NaN-> false

0.0 =-0.01-> true

The basic floating point type and the packaging floating point type have different comparison behaviors.

To make things worse, the rules used to compare NaN and-0 are different between the basic float type and the package class Float. If the float value is equal to the two NaN values, false is returned. If the Float. equals () is used to compare the two NaN Float objects, true is returned. This is because, if not, the NaN Float object cannot be used as a key in HashMap. Similarly, although 0 and-0 are considered equal when they are expressed as floating point values, Float is used. when compareTo () is used to compare the 0 and-0 values of a Float object, the-0 value is smaller than 0.

[Risks in floating point]

Due to the special behavior of infinity, NaN, and 0, when floating point numbers are applied, seemingly harmless conversion and optimization are actually incorrect. For example, although 0.0-f is obviously equal to-f, it is incorrect when f is 0. There are other similar gotcha, some of which are shown in table 2.

Table 2. Invalid floating point assumptions

This expression ...... Not necessarily equal ...... When ......

0.0-f is 0

F <g! (F> = g) f or g is NaN

F = f true f is NaN

F + g-g f g is infinite or NaN

Rounding Error

Floating point operations are rarely accurate. Although some numbers (such as 0.5) can be accurately expressed as binary (base number 2) decimal places (because 0.5 is equal to 2-1), some other numbers (such as 0.1) it cannot be accurately expressed. Therefore, floating-point operations may cause rounding errors, resulting in near-but not equal-possible results. For example, the following simple calculation will get 2.600000000000001 instead of 2.6:

Double s = 0;

For (int I = 0; I <26; I ++)

S + = 0.1;

System. out. println (s );

Similarly, the result produced by multiplying. 1*26 is not equal to the result obtained by adding. 1 itself 26 times. When a floating point number is forcibly converted to an integer, the rounding error is even more serious, because the forced conversion to an integer type will discard the non-integer part, this type of problem exists even for calculations that "seem" to be expected to get an integer. For example, the following statements:

Double d = 29.0*0.01;

System. out. println (d );

System. out. println (int) (d * 100 ));

The following output is displayed:

0.29

28

This may not be what you expected at first.

[Floating Point Number comparison Guide]

Due to the unusual comparative behaviors of NaN and the inevitable rounding error in almost all floating point calculations, it is troublesome to explain the comparison operators of floating point values.

It is best to avoid comparing floating point numbers. Of course, this is not always possible, but you should be aware that you must limit the floating point comparison. If you have to compare floating point numbers to see if they are equal, you should compare the absolute values of their differences with some pre-selected small positive numbers so that what you do is to test whether they are "close enough ". (If you do not know the basic computing scope, you can use the test "abs (a/B-1) <epsilon", which is more accurate than simply comparing the two ). It is even dangerous to test whether a value is greater than zero or smaller than zero. "Think" will generate a calculation of a value slightly greater than zero. In fact, it may be slightly smaller than zero because of the accumulated rounding error..

The unordered nature of NaN makes it more prone to errors when comparing floating point numbers. When comparing floating-point numbers, around the infinite and NaN problems, an empirical rule to avoid gotcha is to explicitly test the value validity, rather than trying to exclude invalid values. In listing 1, there are two possible setter implementations for the feature. This feature can only accept non-negative values. The first implementation will accept NaN, and the second will not. The second form is better, because it explicitly detects the range of values that you think are valid.

Listing 1. Better methods and solutions for non-negative floating point values

// Trying to test by exclusion -- this doesn't catch NaN or infinity

Public void setFoo (float foo ){

If (foo <0)

Throw new IllegalArgumentException (Float. toString (f ));

This. foo = foo;

}

// Testing by loading sion -- this does catch NaN

Public void setFoo (float foo ){

If (foo> = 0 & foo <Float. INFINITY)

This. foo = foo;

Else

Throw new IllegalArgumentException (Float. toString (f ));

}

Do not use floating point values to represent exact values

Some non-integer values (such as a few US dollars and a few US dollars) need to be accurate. Floating point numbers are not precise values, so using them causes rounding errors. Therefore, it is not a good idea to use floating point numbers to represent precise quantities like the amount of currency. Using Floating Point Numbers for dollar and cent calculations can have disastrous consequences. Floating Point Numbers are best used to represent values such as measured values, which are not very accurate from the very beginning.

[BigDecimal]

Since JDK 1.3, Java developers have another numerical representation to represent a non-integer: BigDecimal. BigDecimal is a standard class and does not require special support in the compiler. It can represent decimal places of any precision and calculate them. Internally, BigDecimal can be expressed by a value in any range of precision and a conversion factor. The conversion factor indicates the number of decimal places to move left to obtain the value in the expected range. Therefore, the number expressed in BigDecimal format is unscaledValue * 10-scale.

The addition, subtraction, multiplication, and division methods provide arithmetic operations for BigDecimal values. Since BigDecimal objects are unchangeable, each of these methods generates a new BigDecimal object. Therefore, because of the overhead of object creation, BigDecimal is not suitable for a large number of mathematical calculations, but it is designed to accurately represent decimals. If you are looking for a value that accurately represents the amount of money, BigDecimal can be competent for this task.

All equals methods cannot be tested to be equal.

Similar to the floating point type, BigDecimal has some strange behaviors. Be careful when using the equals () method to check whether values are equal. In the equals () method, two BigDecimal values that indicate the same number but have different values (for example, 100.00 and 100.000) are not equal. However, the compareTo () method considers these two numbers to be equal. Therefore, compareTo () instead of equals () should be used to compare two BigDecimal values ().

In addition, in some cases, decimal operations with any precision still cannot represent precise results. For example, dividing 1 by 9 produces an infinite number of decimal places. 111111 .... For this reason, BigDecimal allows you to explicitly control rounding during Division operations. The movePointLeft () method supports the exact division of 10 power.

Use BigDecimal as the Interchange Type

The SQL-92 includes the DECIMAL data type, which is the exact numeric type used to represent the DECIMAL point, which can perform basic arithmetic operations on the DECIMAL point. Some SQL languages like to call this type NUMERIC. Other SQL languages introduce the MONEY data type. The MONEY data type is defined as a decimal point with two decimal places.

If you want to store the number to the DECIMAL field in the database or retrieve the value from the DECIMAL field, how can you ensure accurate conversion of the number? You may not want to use the setFloat () and getFloat () methods provided by the JDBC PreparedStatement and ResultSet classes, because the conversion between floating point numbers and decimals may lose accuracy. Instead, use the setBigDecimal () and getBigDecimal () Methods of PreparedStatement and ResultSet.

There are several constructors available for BigDecimal. One constructor uses a double-precision floating point number as the input, the other uses an integer and a conversion factor as the input, and the other uses a decimal String as the input. Be careful to use the BigDecimal (double) constructor, because if you do not know about it, it will generate a rounding error during the calculation process. Use an integer or String-based constructor.

Construct the BigDecimal number

There are several constructors available for BigDecimal. One constructor uses a double-precision floating point number as the input, the other uses an integer and a conversion factor as the input, and the other uses a decimal String as the input. Be careful to use the BigDecimal (double) constructor, because if you do not know about it, it will generate a rounding error during the calculation process. Use an integer or String-based constructor.

If the BigDecimal (double) constructor is not appropriate, an exception in the JDBC driver may appear strange when it is passed to the JDBC setBigDecimal () method. For example, consider the following JDBC code that stores the number 0.01 to a decimal field:

PreparedStatement ps = connection. prepareStatement ("insert into Foo SET name = ?, Value =? ");

Ps. setString (1, "penny ");

Ps. setBigDecimal (2, new BigDecimal (0.01 ));

Ps.exe cuteUpdate ();

Some confusing exceptions will be thrown when executing this seemingly harmless code (depending on the specific JDBC driver), because the 0.01 double-precision approximation will lead to a large conversion value, this may confuse the JDBC driver or database. The JDBC driver produces exceptions, but it may not indicate where the code is actually wrong unless you are aware of the limitations of binary floating point numbers. Instead, use BigDecimal ("0.01") or BigDecimal (1, 2) to construct BigDecimal to avoid this problem, because both methods can accurately represent decimal places.

[Conclusion]

Using Floating Point Numbers and Decimals in Java programs is full of traps. Floating-point numbers and decimals are not exactly the same as integers. It cannot be assumed that floating-point Computation must produce integer or precise results, although they do "should. It is best to keep the floating point operation as a non-precise value for calculation, such as measurement. If you want to specify the number of points (for example, the number of US dollars and the number of US Dollars), use BigDecimal.

//////////////////////////////////////// //////////

The following content about floating point numbers