double 的十进制显示精度损失

Loss of precision in decimal display of double

为什么后面的数值显示有出入?

double x = (double)988530483551494912L;
System.out.printf("%f%n", x);           -> 988530483551494910.000000
System.out.println(Double.toString(x)); -> 9.8853048355149491E17
System.out.println(new BigDecimal(x));  -> 988530483551494912

如您所见,toString()%f 转换都丢失了最后一位的精度。但是,我们可以看到最后一位数字实际上是精确的,因为 BigDecimal 转换保留了它。

感谢@user16320675 的评论,我正在回答我自己的问题。原因是数字 988530483551494912L 的精度超出了 double 类型精度的限制,并且 Double.toString() (以及类似的 %f)将根据文档仅使用将 double 数字与相邻数字区分开来所需的最小有效数字位数。相邻数字是指与原始数字的两边具有最小可表示差异的数字。

这可以使用 Math.nextAfter 显示相邻数字来演示:

import static java.lang.Math.nextAfter;
double x = (double)988530483551494912;
System.out.println(nextAfter(x, Double.MIN_VALUE));  ==> 9.8853048355149478E17
System.out.println(x);                               ==> 9.8853048355149491E17
System.out.println(nextAfter(x, Double.MAX_VALUE));  ==> 9.8853048355149504E17

因此,正如我们所见,添加更多有效数字没有意义,因为此字符串表示形式已经有足够的数字来区分数字与相邻值。

但是,还有一个问题:它显示了17位有效数字,但16位就足够了。我不确定为什么它会发出额外的最后一位数字。