ruby浮动根据顺序添加不同的值

时间:2012-12-12 20:18:59

标签: ruby floating-point

所以这很奇怪。我在Ruby 1.9.3中,并且浮动添加无法正常工作。

0.3 + 0.6 + 0.1 = 0.9999999999999999
0.6 + 0.1 + 0.3 = 1

我在另一台机器上试过这个并获得相同的结果。知道为什么会这样吗?

5 个答案:

答案 0 :(得分:12)

浮点运算是不精确的:它们将结果四舍五入到最接近的可表示的浮点值 这意味着每个浮点运算都是:

float(a op b) = mathematical(a op b) + rounding-error( a op b )

如上式所示,舍入误差取决于操作数a&湾
因此,如果您以不同的顺序执行操作,

float(float( a op b) op c) != float(a op (b op c))

换句话说,浮点运算不是关联的 它们是可交换的......

正如其他人所说,将十进制表示0.1(即1/10)转换为基数2表示(即1/16 + 1/64 + ...)将导致无限的数字系列。因此,float(0.1)不等于1/10,它也有一个舍入误差,它导致一系列二进制数字,这说明后面的操作有一个非零舍入误差(数学结果不可表示)在浮点)

答案 1 :(得分:4)

之前已多次说过,但需要重复一遍:浮点数本质上是近似的十进制数。由于浮点数以二进制形式存储的方式,有些十进制数无法精确表示。 会发生小但可察觉的舍入错误。

为了避免这种混乱,您应该始终将您的数字格式化为适当数量的地方进行演示:

 '%.3f' % (0.3 + 0.6 + 0.1)
 # => "1.000" 

 '%.3f' % (0.6 + 0.1 + 0.3)
 # => "1.000" 

这就是为什么使用货币值的浮点数是有风险的,并且通常鼓励您使用定点数字或常规整数来处理这些事情。

答案 2 :(得分:3)

首先,源文本中的数字“0.3”,“。6”和“.1”将转换为浮点数,我将其称为 a b c 。这些值接近.3,.6和.1但不等于它们,但这并不是您看到不同结果的直接原因。

在每个浮点算术运算中,可能会有一些舍入误差,一些小数 e i 。所以你的两个表达式计算的确切数学结果是:

  

a + b + e 0 )+ c + e 1 和   ( b + c + e 2 )+ a + e 3

也就是说,在第一个表达式中, a 被添加到 b ,并且存在轻微的舍入错误 e 0 。然后添加 c ,并且存在轻微的舍入错误 e 1 。在第二个表达式中, b 被添加到 c ,并且存在轻微的舍入错误 e 2 。最后,添加了 a ,并且存在轻微的舍入错误 e 3

您的结果不同的原因是 e 0 + e 1 e 2 + e 3 。也就是说,添加 a b 时所需的舍入与 b c时所需的舍入不同被添加和/或在两个案例的第二次添加中所需的舍入是不同的。

有一些规则可以控制这些错误。如果您了解这些规则,则可以对它们进行推断,以限制最终结果中的错误大小。

答案 3 :(得分:1)

这是浮点数的常见限制,因为它们被编码在基数2而不是基数10中。可能很难理解,但是一旦你这样做,你就可以轻松避免这样的问题。我推荐this guide,深入解释它。

具体来说,对于这个问题,您可以尝试将结果四舍五入到最接近的百万分之一位置:

result = (0.3+0.6+0.1)
  => 0.9999999999999999
(result*1000000.0).round/1000000.0
  => 1.0

至于为什么订单很重要,它与舍入有关。当这些数字变成浮点数时,它们会转换为二进制数,并且所有数字都变成重复分数,例如⅓是十进制数。由于在每次添加期间结果都会舍入,因此最终答案取决于添加的顺序。似乎在其中一个中,你得到了一个圆形,而在另一个中,你得到一个向下的圆形。这解释了这种差异。

值得注意的是这两个答案之间的实际差异:约0.0000000000000001。

答案 4 :(得分:1)

在视图中,您还可以使用number_with_precision帮助程序:

result = 0.3 + 0.6 + 0.1
result = number_with_precision result, :precision => 3