为什么改变求和顺序会返回不同的结果?

23.53 + 5.88 + 17.64 = 47.05

23.53 + 17.64 + 5.88 = 47.050000000000004

Java和JavaScript都返回相同的结果。

我知道,由于浮点数用二进制表示的方式,一些有理数(如1/3 - 0.333333…)不能精确地表示。

为什么仅仅改变元素的顺序就会影响结果?


当前回答

为了给这里的其他答案添加一个不同的角度,这个SO答案表明,有一些方法可以进行浮点数学,其中所有求和顺序在位级上返回完全相同的值。

其他回答

浮点数使用IEEE 754格式表示,该格式为尾数(有效位)提供了特定的位大小。不幸的是,这给了你一个特定数量的“分数构建块”来玩,某些分数值不能精确地表示。

在您的情况下发生的情况是,在第二种情况下,由于计算加法的顺序,加法可能会遇到一些精度问题。我没有计算过具体的数值,可能是23.53 + 17.64不能精确表示,而23.53 + 5.88可以。

不幸的是,这是一个已知的问题,你必须处理。

也许这个问题很愚蠢,但是为什么仅仅改变元素的顺序就会影响结果呢?

它将根据值的大小更改值舍入的点。作为我们正在看到的事情的一个例子,让我们假设我们使用的不是二进制浮点数,而是具有4位有效数字的小数点浮点类型,其中每个加法都以“无限”精度执行,然后舍入到最接近的可表示数字。这里有两个总和:

1/3 + 2/3 + 2/3 = (0.3333 + 0.6667) + 0.6667
                = 1.000 + 0.6667 (no rounding needed!)
                = 1.667 (where 1.6667 is rounded to 1.667)

2/3 + 2/3 + 1/3 = (0.6667 + 0.6667) + 0.3333
                = 1.333 + 0.3333 (where 1.3334 is rounded to 1.333)
                = 1.666 (where 1.6663 is rounded to 1.666)

我们甚至不需要非整数来解决这个问题:

10000 + 1 - 10000 = (10000 + 1) - 10000
                  = 10000 - 10000 (where 10001 is rounded to 10000)
                  = 0

10000 - 10000 + 1 = (10000 - 10000) + 1
                  = 0 + 1
                  = 1

This demonstrates possibly more clearly that the important part is that we have a limited number of significant digits - not a limited number of decimal places. If we could always keep the same number of decimal places, then with addition and subtraction at least, we'd be fine (so long as the values didn't overflow). The problem is that when you get to bigger numbers, smaller information is lost - the 10001 being rounded to 10000 in this case. (This is an example of the problem that Eric Lippert noted in his answer.)

需要注意的是,右边第一行的值在所有情况下都是相同的——因此,尽管理解十进制数(23.53,5.88,17.64)不会精确地表示为双精度值很重要,但这只是因为上面所示的问题而造成的问题。

这实际上涵盖的不仅仅是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的结果。

我认为这与评估的顺序有关。虽然和在数学世界中自然是一样的,但在二进制世界中,它不是a + B + C = D,而是

A + B = E
E + C = D(1)

这是第二步,浮点数可以脱离。

当你改变顺序时,

A + C = F
F + B = D(2)

为了给这里的其他答案添加一个不同的角度,这个SO答案表明,有一些方法可以进行浮点数学,其中所有求和顺序在位级上返回完全相同的值。