简体   繁体   中英

Floating point precision in literals vs calculations

I'm wondering why floating point numbers in Java can represent exact value when they are initialized as literals, but they are approximate when they represent result of some calculation. For example:

double num1 = 0.3;
double num2 = 0.1 + 0.2;
System.out.println(num1);
System.out.println(num2);

why the result is:

0.3
0.30000000000000004

and not:

0.30000000000000004
0.30000000000000004

When there is no exact binary representation of 0.3. I know the BigDecimal class, but I don't quite understand this primitive numbers inconsistency.

None of the three numbers can be represented exactly as a double . The reason that you get different results is that the value after adding 0.1 to 0.2 has a different representation error than 0.3 . The difference of about 5.5E-17 is enough to cause a difference when printing out the result ( demo ).

double a = 0.2;
double b = 0.1;
double c = 0.3;
double d = a+b;
double e = d-c; //  This is 5.551115123125783E-17

When 0.3 is converted to its representation as ones and zeroes then converted back to decimal, it rounds to 0.3. However, when 0.1 and 0.2 are respectively converted to binary, the errors add up upon addition so as to show up when the sum is converted back to decimal. A thorough explanation would involve demonstrating the IEEE representation of each number along with the addition and conversions. A bit involved, but I hope you got the idea.

The addition itself cannot produce an exact representation of 0.3 , hence printing the result of 0.1 + 0.2 yields 0.30000000000000004 .

On the other hand, when calling System.out.println(0.3); , the println(double) method will perform some rounding on the result: it eventually calls Double.toString(double) which mentions that the result is approximate:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

If you use a BigDecimal the difference can be seen:

System.out.println(0.3);  // 0.3
System.out.println(new BigDecimal(0.3));  // 0.299999999999999988897769753748434595763683319091796875

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM