Strange floating-point behaviour in a Java program
本问题已经有最佳答案,请猛点这里访问。
在我的程序中,我有一个数组,25个双值0.04当我试图在一个循环中求和这些值时,我得到以下结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 0.0 + 0.04 = 0.04 0.04 + 0.04 = 0.08 0.08 + 0.04 = 0.12 0.12 + 0.04 = 0.16 0.16 + 0.04 = 0.2 0.2 + 0.04 = 0.24000000000000002 0.24000000000000002 + 0.04 = 0.28 0.28 + 0.04 = 0.32 0.32 + 0.04 = 0.36 0.36 + 0.04 = 0.39999999999999997 0.39999999999999997 + 0.04 = 0.43999999999999995 0.43999999999999995 + 0.04 = 0.4799999999999999 0.4799999999999999 + 0.04 = 0.5199999999999999 0.5199999999999999 + 0.04 = 0.5599999999999999 0.5599999999999999 + 0.04 = 0.6 0.6 + 0.04 = 0.64 0.64 + 0.04 = 0.68 0.68 + 0.04 = 0.7200000000000001 0.7200000000000001 + 0.04 = 0.7600000000000001 0.7600000000000001 + 0.04 = 0.8000000000000002 0.8000000000000002 + 0.04 = 0.8400000000000002 0.8400000000000002 + 0.04 = 0.8800000000000002 0.8800000000000002 + 0.04 = 0.9200000000000003 0.9200000000000003 + 0.04 = 0.9600000000000003 |
为什么会发生这种事?!
编程语言中最常见的浮点值存储——IEEEsingles和double——对大多数小数部分没有精确的表示。
原因是它们以二进制浮点格式而不是十进制浮点格式存储值。唯一可以精确表示的分数值是两个负幂之和。像:
- 0.5(2 ^ - 1)
- 0.125(2 ^ - 3)
- 0.625(2^-1+2^-3)
等。
你看到的事实是,0.96这样的数字表示并不完全可以表示,因为它们不能表示为2的负幂之和。因此,当以全精度作为小数部分打印出来时,它们将与原始值不匹配。
另见"每个计算机科学家都应该知道什么是浮点"
其他答案提到了原因,但没有提到如何避免。
有几种解决方案:
- 缩放:如果所有的数字都是0.01的倍数(例如),则将所有的数字都乘以100,然后使用整数算术(精确)。
- numeric type:如果您的语言有一个numeric类型(如SQL中的
numeric 类型),您可以使用它。 - 任意精度理性:使用像gmp这样的bignum库,它允许您将这些数字表示为两个整数的比率。
- 小数浮点数:如果您有一个像IEEE-754R中的小数浮点数,您可以使用它。
您可能希望将Java BigDigMax类签出为浮动和双倍的替代品。