Java程序中奇怪的浮点行为

Ste*_*lla 10 java math

在我的程序中,我有一个25个双值的数组0.04当我尝试在循环中求和这些值时,得到以下结果:

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
Run Code Online (Sandbox Code Playgroud)

为什么会发生这种情况?!

Bar*_*lly 23

编程语言中最常见的浮点值存储 - IEEE单一和双精度 - 对大多数小数部分没有精确的表示.

原因是它们以二进制浮点格式存储值,而不是十进制浮点格式.唯一可以精确表示的小数值是两个负幂之和.数字如:

  • 0.5(2 ^ -1)
  • 0.125(2 ^ -3)
  • 0.625(2 ^ -1 + 2 ^ -3)

等等.

你所看到的是,像0.96这样的数字表示并不完全可以表示,因为它们不能表示为两个负幂的总和.因此,当以完整精度打印出小数部分时,它们将与原始值不匹配.


Joh*_*fle 14

另请参阅" 每个计算机科学家应该知道的浮点数 "

  • 它仍然是人们应该知道的东西,即使它太密集了,他们也无法阅读.;) (4认同)

Ces*_*arB 5

其他答案提到了原因,但没有提到如何避免它.

有几种解决方案:

  • 缩放:如果所有数字都是0.01的倍数(例如),则将所有数字乘以100并使用整数算术(这是精确的).
  • 数字类型:如果您的语言具有数字类型(如numericSQL中的类型),则可以使用它.
  • 任意精度定理:使用像GMP这样的bignum库,它允许您将这些数字表示为两个整数的比率.
  • 十进制浮点:如果您有像IEEE-754r中的十进制浮点,则可以使用它.