2008-11-29 80 views
9

在我的节目,我有一个数组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 

为什么地球上会发生吗?!

+0

有点相关的问题:http://stackoverflow.com/questions/327020/why-are-floating-point-values-so-prolific – CesarB 2008-11-29 14:36:51

回答

23

编程语言中最常见的浮点值存储 - IEEE singles and doubles - 对大多数小数没有精确表示。

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

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

等等

你所看到的是这样一个事实,即像0.96这样的数字的表示不能完全表示,因为它们不能表示为两个负幂的和。因此,当作为小数部分以完全精确度打印出来时,它们不会与原始值匹配。

+0

“而不是小数”有点错误的想法。如果它使用了十进制,它将被限制为小数值,这是10的负幂的和,所以这不会真正解决太多。有人读到你的答案可能会得出这样的想法:“如果只有他们使用base10” – jalf 2008-11-29 14:59:01

+0

@jalf,这个问题就会消失,但至少有一个十进制浮点表示会让更少的人用于简单的口袋会计。增加一堆20个镍币而不是完全降价对许多人来说是令人惊讶的。一个十进制符号会让这种情况变得正确,并且用有趣的问题来交换那些应该从小学课程算术课程中熟悉的数字。 – RBerteig 2009-06-12 23:27:44

5

其他的答案中提到为什么,但不知道如何避免它。

有几种解决方案:

  • 缩放:如果您所有的数字是0.01的倍数(例如),乘以100的一切,使用整数运算(这是精确的)。
  • 数字类型:如果您的语言具有数字类型(如SQL中的numeric类型),则可以使用它。
  • 任意精度的理由:使用像GMP这样的数据库,它允许您将这些数字表示为两个整数的比率。
  • 十进制浮点数:如果你有一个像IEEE-754r那样的十进制浮点数,你可以使用它。
1

您可能希望查看java BigDecimal类作为浮动和双打的替代方法。