文字与计算中的浮点精度

Floating point precision in literals vs calculations

我想知道为什么Java中的浮点数在初始化为文字时可以表示精确值,但在表示某些计算结果时它们是近似值。 例如:

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

为什么结果是:

0.3
0.30000000000000004

而不是:

0.30000000000000004
0.30000000000000004

当没有 0.3 的精确二进制表示时。 我知道BigDecimal class,但我不太明白这种原始数字不一致。

加法本身无法生成 0.3 的精确表示,因此打印 0.1 + 0.2 的结果会产生 0.30000000000000004.

另一方面,当调用 System.out.println(0.3); 时,println(double) 方法将对结果进行一些舍入:它最终调用 Double.toString(double),其中提到结果是近似值:

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.

如果使用 BigDecimal 可以看出区别:

System.out.println(0.3);  // 0.3
System.out.println(new BigDecimal(0.3));  // 0.299999999999999988897769753748434595763683319091796875
三个数中的

None可以精确表示为double。您得到不同结果的原因是将 0.1 添加到 0.2 后的值与 0.3 具有不同的表示错误。大约5.5E-17的差异足以在打印结果时造成差异(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

当 0.3 转换为 1 和 0 的表示形式,然后再转换回十进制时,它四舍五入为 0.3。 但是,当0.1和0.2分别转换为二进制时,错误会在相加时累加,以便在和转换回十进制时显示出来。 详尽的解释将涉及演示每个数字的 IEEE 表示以及加法和转换。有点复杂,但我希望你明白了。