Tips and traps in floating point and decimal places

Source: Internet
Author: User
Tips and traps in floating point and decimal places

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 are easy to ignorejava.math.BigDecimalDecimals of any precision provided-most applications do not use them. However, in an integer-based program, it is sometimes unexpectedly necessary to represent non-integer data. For example, JDBCBigDecimalAs SQLDECIMALThe preferred interchange format of columns.

 

JAVA supports two basic floating point types:floatAnddoubleAnd their corresponding packaging classesFloatAndDouble. 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 of 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

 

Except for the standard range of values allowed by encoding (float, From 1.4e-45 to 3.4028235e + 38), some also indicate infinity, negative infinity,-0And Nan (which represents "not a number. These values exist in order to give a square root to a negative number in the case of an error condition (for example, Arithmetic overflow, divide0), You can use a number in the floating point value set to represent the result.

These special numbers have some unusual characteristics. For example,0And-0They are different values, but they are considered equal when compared to whether they are equal. Divide the number by a non-zero number and the result is equal0. The special number Nan is unordered.==,<And>When the operator compares nan with other floating point values, the result isfalse. IffIf it is Nan(f == f)You will also getfalse. If you want to compare the floating point value with Nan, useFloat.isNaN()Method. Table 1 shows the infinite and Nan attributes.

 

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

To make things worsefloatTypes and packagesFloatUsed to compare Nan and-0The rules are different. ForfloatTo compare the two Nan values.falseAnd useFloat.equals()To compare two NanFloatThe object will gettrue. The reason for this is that, if not, NanFloatObject usedHashMap. Similarly, although0And-0It is considered equal when expressed as a floating point value,Float.compareTo()To compareFloatObject0And-0Will display-0Less0.

Due to infinity, Nan, and0When floating point numbers are applied, the seemingly harmless conversion and optimization are actually incorrect. For example0.0-fObviously equal-f, But whenfIs0This is incorrect. There are other similar gotcha, some of which are shown in table 2.

 

This expression ...... Not necessarily equal ...... When ......
0.0 - f -f F is0
f < g ! (f >= g) F or G is Nan
f == f true F is Nan
f + g - g f G is infinite or Nan

 

Floating point operations are rarely accurate. Although some numbers (such0.5) Can be accurately expressed as binary (base number 2) decimal (because0.5Equal to 2-1), but some other numbers (such0.1. Therefore, floating-point operations may cause rounding errors, resulting in near-but not equal-possible results. For example, the following simple calculation will produce2.600000000000001Instead2.6:

 double s=0;  for (int i=0; i<26; i++)    s += 0.1;  System.out.println(s);

Similarly,.1*26The result of multiplication is not equal.1The result obtained by adding it 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.

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.

// 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 ));
}
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.

 

Since JDK 1.3, Java developers have another numerical representation to represent non-integers:BigDecimal.BigDecimalIt 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, values in any range of precision and a conversion factor can be used to representBigDecimal, The conversion factor indicates the number of decimal places to move left to obtain the value in the expected range. ThereforeBigDecimalThe number is expressedunscaledValue*10 -scale.

For addition, subtraction, multiplication, and DivisionBigDecimalValue provides arithmetic operations. BecauseBigDecimalObjects are immutable, and each of these methods produces a newBigDecimalObject. Therefore, because of the overhead of object creation,BigDecimalIt is not suitable for a large number of mathematical calculations, but is designed to accurately represent decimals. If you are looking for a value that accurately represents the amount of moneyBigDecimalCan be well qualified for this task.

 

Similar to the floating point type,BigDecimalThere are also some strange behaviors. Especially in useequals()To check whether the values are equal.equals()The method assumes that the two indicate the same number but the conversion value is different (for example,100.00And100.000)BigDecimalThe values are not equal. However,compareTo()The method considers the two numbers to be equal, so we can compare the two numbers.BigDecimalValue, you should usecompareTo()Insteadequals().

In addition, in some cases, decimal operations with any precision still cannot represent precise results. For example,1Divided9Returns an infinite number of decimal places..111111.... For this reason, when performing Division operations,BigDecimalYou can explicitly control rounding.movePointLeft()The method supports the exact division of 10 power.

 

SQL-92 includesDECIMALData type, which is an exact number used to represent the decimal point. It can perform basic arithmetic operations on decimal places. Some SQL languages like to call this typeNUMERICType, other SQL languages introduceMONEYData type. The money data type is defined as a decimal place with two decimal places on the right.

If you want to store numbers in the databaseDECIMALField, or fromDECIMALHow to accurately convert a field to a value? You may not want to use JDBCPreparedStatementAndResultSetClasssetFloat()AndgetFloat()Method, because the conversion between floating point numbers and decimal places may lose accuracy. Instead, usePreparedStatementAndResultSetOfsetBigDecimal()AndgetBigDecimal()Method.

ForBigDecimalThere are several available constructors. 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 numberStringInput. Be careful when usingBigDecimal(double)Constructor, because if you do not know it, it will produce a rounding error during the calculation process. Use an integer orString Constructor.

 

ForBigDecimalThere are several available constructors. 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 numberStringInput. Be careful when usingBigDecimal(double)Constructor, because if you do not know it, it will produce a rounding error during the calculation process. Use an integer orStringConstructor.

If you useBigDecimal(double)The constructor is incorrect.setBigDecimal()Method may cause exceptions in the seemingly strange JDBC driver. For example, consider the following JDBC code.0.01Stored in decimal field:

 PreparedStatement ps =    connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");  ps.setString(1, "penny");  ps.setBigDecimal(2, new BigDecimal(0.01));  ps.executeUpdate();

Some confusing exceptions will be thrown when executing this seemingly harmless code (depending on the specific JDBC driver), because0.01Double-precision approximation may lead to a large conversion value, which 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, useBigDecimal("0.01")OrBigDecimal(1, 2)StructureBigDecimalTo avoid this problem, because both methods can accurately represent decimals.

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. UseBigDecimal.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.