为什么0.1 + 0.4 = 0.5?

7
我们知道浮点数存在问题,因为十进制数字并不能在二进制中完全准确地表示。它们被四舍五入成一个可以在二进制中表示的数字;有时这个数字会更高,有时则更低。在这种情况下,使用普遍的IEEE 754双精度格式,0.1和0.4都会向上舍入。
0.1 = 0.1000000000000000055511151231257827021181583404541015625
0.4 = 0.40000000000000002220446049250313080847263336181640625

由于这两个数字都很高,你期望它们的总和也很高。完美的加法应该给出0.5000000000000000277555756156289135105907917022705078125,但实际上你得到了一个精确的0.5。为什么呢?


这个问题浮点数运算是否存在问题?已经在上面提到了,但这个问题不同。它要求在考虑那个问题的答案的基础上进一步详细地解释一个非直观的结果。


9
我们知道浮点数存在问题,但我们知道的是程序员对浮点数算术的理解存在问题,因此不能说浮点数本身有问题。由于这篇文章似乎是一份权威的问答集,我认为不应该以这样具有误导性的陈述作为开端。 - High Performance Mark
@HighPerformanceMark 我需要一种方式来表明这不是您典型的浮点精度问题,也许我有点过于夸张了。而且它并不是真正意图成为规范,这是一个真正的问题,有人问我,我很难想出一个答案。但只要我有一个答案,我想我会呈现它,并让它与其他答案竞争。 - Mark Ransom
2个回答

5
这个计算方式的原因是加法使结果进入另一个(二进制)数量级。这在左边(最高位)添加了一个重要的比特,因此必须从右边(最低位)删除一位。
第一个数字0.1在二进制中存储为介于2^-4==1/16和2^-3==1/8之间的数字。第二个数字0.4在二进制中存储为介于2^-2==1/4和2^-1==1/2之间的数字。和为0.5是数字2^-1==1/2或稍大一点的数字。这是数量级不匹配可能导致丢失数字的情况。
这里有一个更易理解的例子。假设我们正在使用可以在浮点数中存储四个小数位的十进制计算机。也让我们假设我们想要相加的数字是10/3和20/3。这些可能最终存储为
3.334

并且

6.667

这两个数字都有点高。当我们得到这些数字时,我们预计它们的总和也会有点高,即

10.001

请注意,我们的结果已经进入了一个新的数量级。完整的结果有五个小数位,无法容纳。因此,计算机将结果四舍五入到只有四个小数位,并得到总和。

10.00

令人惊讶的是,10/3 + 20/3 的确是正确的精确答案。

在美国高中的化学和物理课程中,我经常遇到这样的情况。当计算移动到一个新的数量级时,精度和有效数字会发生奇怪的变化。


我喜欢十进制的例子,它使得这个案例更易于理解。我只希望它能够启发而不是混淆。 - Mark Ransom
即使没有“将结果推向另一个数量级”的因素,我认为“与偶数相连”的规则也会将结果推向0.5。 - plugwash

2
尽管大多数十进制数字需要舍入以适应二进制,但有些不需要。因为0.5是2的-1次方,所以可以在二进制中精确表示。
浮点数不仅是二进制,还具有有限的精度。下面是精确的总和以及两个最接近的IEEE 754双精度可表示数字:
0.5000000000000000277555756156289135105907917022705078125
0.5000000000000000000000000000000000000000000000000000000
0.5000000000000001110223024625156540423631668090820312500

很明显,精确的0.5最接近真实总和。IEEE 754有关于简单数学运算的规则,规定了结果舍入的方式,通常可以依赖于最接近的结果。


1
我也不认为这是针对标题中提出的问题一个非常好的规范答案。它没有提及最接近“0.1”和“0.4”的浮点数的十进制表示。 - High Performance Mark
@HighPerformanceMark 我并不打算让它成为规范,如果您能更好地解释它,请留下答案! - Mark Ransom
2
感谢邀请我把头伸进这只狮子的嘴里。我不确定 Stack Overflow 或整个互联网是否需要另一篇解释浮点运算与我们在学校学到的十进制运算不同之处的文章。更不确定我是否是写这篇文章的合适人选。 - High Performance Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接