我怀疑区别在于从 80 位浮点值到 long 的转换与从 80 位浮点值到 64 位的转换以及然后到的转换很长。
(出现 80 位的原因是,这是用于实际算术的典型精度,以及浮点寄存器的宽度。)
假设 80 位结果类似于 10.999999999999999 - 从它到 long 的转换产生 10。但是,最接近 80 位值的 64 位浮点值实际上是 11.0,所以两阶段转换最终产生 11。
编辑:给这个更多的重量......
这是一个使用任意精度算术进行相同计算的 Java 程序。请注意,它将最接近 0.1 的双精度值转换为 BigDecimal - 该值为 0.1000000000000000055511151231257827021181583404541015625。 (换句话说,计算的确切结果无论如何不是 11。)
import java.math.*;
public class Test
{
public static void main(String[] args)
{
BigDecimal c = new BigDecimal(0.1d);
BigDecimal a = new BigDecimal(1d);
BigDecimal b = new BigDecimal(2d);
BigDecimal result = b.subtract(a)
.add(c)
.divide(c, 40, RoundingMode.FLOOR);
System.out.println(result);
}
}
结果如下:
10.9999999999999994448884876874217606030632
换句话说,大约 40 位十进制数字是正确的(远远超过 64 位或 80 位浮点可以处理的数量)。
现在,让我们考虑一下这个数字在二进制中的样子。我没有任何工具可以轻松进行转换,但我们可以再次使用 Java 来提供帮助。假设一个标准化的数字,“10”部分最终使用三位(11 比 1 = 1011)。剩下 60 位尾数用于扩展精度(80 位)和 48 位用于双精度(64 位)。
那么,每个精度中最接近 11 的数字是多少?再次,让我们使用 Java:
import java.math.*;
public class Test
{
public static void main(String[] args)
{
BigDecimal half = new BigDecimal("0.5");
BigDecimal eleven = new BigDecimal(11);
System.out.println(eleven.subtract(half.pow(60)));
System.out.println(eleven.subtract(half.pow(48)));
}
}
结果:
10.999999999999999999132638262011596452794037759304046630859375
10.999999999999996447286321199499070644378662109375
所以,我们得到的三个数字是:
Correct value: 10.999999999999999444888487687421760603063...
11-2^(-60): 10.999999999999999999132638262011596452794037759304046630859375
11-2^(-48): 10.999999999999996447286321199499070644378662109375
现在为每个精度计算出最接近正确值的值 - 对于扩展精度,它小于 11。将这些值中的每一个四舍五入为 long,最终分别得到 10 和 11。
希望这是足以说服怀疑者的证据;)