为什么改变求和顺序会返回不同的结果?
23.53 + 5.88 + 17.64 = 47.05
23.53 + 17.64 + 5.88 = 47.050000000000004
Java和JavaScript都返回相同的结果。
我知道,由于浮点数用二进制表示的方式,一些有理数(如1/3 - 0.333333…)不能精确地表示。
为什么仅仅改变元素的顺序就会影响结果?
为什么改变求和顺序会返回不同的结果?
23.53 + 5.88 + 17.64 = 47.05
23.53 + 17.64 + 5.88 = 47.050000000000004
Java和JavaScript都返回相同的结果。
我知道,由于浮点数用二进制表示的方式,一些有理数(如1/3 - 0.333333…)不能精确地表示。
为什么仅仅改变元素的顺序就会影响结果?
当前回答
浮点数使用IEEE 754格式表示,该格式为尾数(有效位)提供了特定的位大小。不幸的是,这给了你一个特定数量的“分数构建块”来玩,某些分数值不能精确地表示。
在您的情况下发生的情况是,在第二种情况下,由于计算加法的顺序,加法可能会遇到一些精度问题。我没有计算过具体的数值,可能是23.53 + 17.64不能精确表示,而23.53 + 5.88可以。
不幸的是,这是一个已知的问题,你必须处理。
其他回答
浮点数使用IEEE 754格式表示,该格式为尾数(有效位)提供了特定的位大小。不幸的是,这给了你一个特定数量的“分数构建块”来玩,某些分数值不能精确地表示。
在您的情况下发生的情况是,在第二种情况下,由于计算加法的顺序,加法可能会遇到一些精度问题。我没有计算过具体的数值,可能是23.53 + 17.64不能精确表示,而23.53 + 5.88可以。
不幸的是,这是一个已知的问题,你必须处理。
我认为这与评估的顺序有关。虽然和在数学世界中自然是一样的,但在二进制世界中,它不是a + B + C = D,而是
A + B = E
E + C = D(1)
这是第二步,浮点数可以脱离。
当你改变顺序时,
A + C = F
F + B = D(2)
这实际上涵盖的不仅仅是Java和Javascript,而且可能会影响任何使用浮点数或双精度数的编程语言。
在内存中,浮点数使用一种遵循IEEE 754的特殊格式(转换器提供的解释比我更好)。
不管怎样,这是浮点转换器。
http://www.h-schmidt.net/FloatConverter/
运算的顺序是运算的“精细度”。
第一行由前两个值得到29.41,指数是2^4。
第二行得到41.17,指数是2^5。
随着指数的增加,我们将失去一个重要的数字,这很可能会改变结果。
试着在最右边的最后一位上敲开和关闭41.17,你可以看到像指数的1/2^23这样“微不足道”的东西足以导致这个浮点数的差异。
编辑:对于那些记得重要数字的人来说,这应该属于这一类。10^4 + 4999,有效数为1,等于10^4。在这种情况下,显著数字要小得多,但是我们可以看到附加了.00000000004的结果。
乔恩的回答当然是正确的。在你的例子中,误差不会比你做任何简单浮点运算所积累的误差大。在这种情况下,一种情况下误差为零而另一种情况下误差很小;这其实不是一个有趣的场景。一个很好的问题是:是否存在这样的情况:改变计算的顺序会从一个微小的错误变成一个(相对)巨大的错误?答案无疑是肯定的。
举个例子:
x1 = (a - b) + (c - d) + (e - f) + (g - h);
vs
x2 = (a + c + e + g) - (b + d + f + h);
vs
x3 = a - b + c - d + e - f + g - h;
显然,在精确的算术中,它们是相同的。试图找出a, b, c, d, e, f, g, h的值,使得x1, x2和x3的值相差很大,这是很有趣的。看看你能不能做到!
为了给这里的其他答案添加一个不同的角度,这个SO答案表明,有一些方法可以进行浮点数学,其中所有求和顺序在位级上返回完全相同的值。