为什么这个减法不等于零?

7

17
在创建 Stack Overflow 账户时,应该设置一个浮点数测试题。我认为这是该网站上最常见的问题。 - recursive
3
@recursive:http://meta.stackexchange.com/questions/26621/whats-the-most-repeated-question-on-stackoverflow/26633#26633 - Michael Myers
1
当我学习编程时,浮点数中缺乏精度的问题在第一次讨论浮点数时就被提到了,并且此后多次提及。他们现在不再这样做了吗? - Paul Tomblin
哈哈,Haskell 里也有同样的 bug,它在这些方面通常做得很好:Prelude> 416582.2850 - 411476.8100 - 5105.475 -2.3646862246096134e-11 - Jamie McCrindle
3
“-2.36468622461E-011?”在我所在的社区,它被称为“零”。 - President James K. Polk
我还记得我们的一位初级开发人员第一次被浮点精度绊倒时的情形。抹泪 它们成长的如此之快! - Dana
7个回答

17

1
5105.475 是一个无法在双精度中精确表示的数字。PrecisionEvaluate 是我需要使用的 ColdFusion 函数。 - Brian Pan
实际上,这些数字都无法完全用双精度值表示。 - Stephen Canon
+1 优秀的参考资料,还可以查看 G.L. Steele 的《如何准确打印浮点数》。 - D.Shawley
1
我今天早上读到了这段话,我发誓:“如果我能回到过去改变一件事,我可能会尝试让一些早期的文盲人不要在计数时使用他们的拇指。这可能已经成为标准,并且在现代化时代会使很多事情变得更容易。另一方面,我们从与十进制和二的幂不兼容的斗争中学到了很多东西。”-Guy Steele,《编码人员的工作》 - Brian Pan
尽管八进制对计算机来说可能很方便,但我建议如果我们每只手都有六个手指(十二进制),那么我们的数字系统整体上会更好。12比10有更多的小因子。 - Greg Hewgill

7

浮点数不准确(实数有无限多个,但只有有限数量的32位或64位数字来表示它们)。

如果您不能处理微小的误差,则应改用BigDecimal


但是416582.2850 - 411476.8100可以被精确地表示为5105.475。这些数字仅有4位小数,不重复。4位小数是否太精确? - Brian Pan
2
不是十进制表示法必须是有限的,而是二进制表示法必须是有限的。请参阅http://en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binary,了解哪些分数可以在二进制中准确表示(只有分母中有2作为质因子的分数可以)。 - Michael Myers
@Brian:问题不在于小数点后的位数。分数1/3在十进制中无法表示,对吧?它会变成3.3333333循环。同样的情况也适用于二进制(基数为2)中的小数(或分数)。虽然在十进制中有效,但1.1在二进制中无法准确表示,因此会导致所谓的“舍入误差”。 - Sasha Chedygov

6

在ColdFusion中使用PrecisionEvaluate()(它将使用Java中的BigDecimal

zero = PrecisionEvaluate(416582.2850 - 411476.8100 - 5105.475);

Evaluate() 不同,不需要使用 ""。

2
这个“bug”实际上并不是真正的bug,而是浮点数算法的工作原理。详见:http://docs.sun.com/source/806-3568/ncg_goldberg.html 如果你想在Java中实现任意精度,可以使用BigDecimal
    BigDecimal a = new BigDecimal("416582.2850");
    BigDecimal b = new BigDecimal("411476.8100");
    BigDecimal c = new BigDecimal("5105.475");
    System.out.println(a.subtract(b).subtract(c)); // 0.0

2

由于计算机以二进制存储数字,因此浮点数是不精确的。1E-11 是因为将这些十进制数四舍五入到最接近的可表示二进制数而产生的微小差异。


1

这些是浮点数问题,使用 BigDecimal 可以解决。

在 Google 中改变减法的顺序也会得到零。

416582.2850 - 5105.475 - 411476.8100 = 0

1
问题在于浮点类型的不精确表示。由于这些无法完全表示为浮点数,因此会出现一些精度损失,导致操作产生小误差。通常情况下,对于浮点数,您希望比较结果是否等于另一个值,误差范围在某个小的 epsilon(误差因子)内。

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