Ruby Float#round 方法在 round(2) 中行为不正确

Ruby Float#round method behaves incorrectly with round(2)

提问人:Gautam Rege 提问时间:9/17/2012 最后编辑:sawaGautam Rege 更新时间:9/17/2012 访问量:877

问:

我了解到建议使用 代替 ,但这个要么是一个错误,要么突出了 的深奥性质。“1.015”、“1.025”和“1.035”似乎有问题。BigDecimalFloatFloatFloat#round(2)

1.015.round(2)
 => 1.01    # => WRONG .. should be 1.02
1.025.round(2)
 => 1.02    # => WRONG .. should be 1.03
1.035.round(2)
 => 1.03    # => WRONG .. should be 1.04
1.045.round(2)
 => 1.05    # => CORRECT
1.016.round(2)
 => 1.02    # => CORRECT

round(3)工作正常。

1.0015.round(3)
 => 1.002  # => CORRECT
1.235.round(2)
 => 1.24   # => CORRECT 

为了在 Rails 应用程序中修补它,我这样做了:

config/initializers/float_mp.rb

require 'bigdecimal'

class Float
  def round(val=0)
     BigDecimal.new(self.to_s).round(val).to_f
  end
end

这似乎是一种奇怪且昂贵的解决方法。这可能是一个错误吗?Float#round

Ruby 浮点精度 ruby-1.9.3

评论

1赞 Mysticial 9/17/2012
不幸的是,这就是二进制浮点的工作方式。如果你想要如此严格的行为,你的“昂贵的措辞”就是要做的事情。
0赞 Mark Dickinson 9/18/2012
您正在寻找可预测的二进制近似值,以将二进制近似值四舍五入到十进制中途情况的结果。那条路就是疯狂!

答:

5赞 janneb 9/17/2012 #1

AFAICS ruby round() 工作正常。无论如何,它可能只是 libm 中 round() 函数的包装器。

因此,原因是浮点文字不能完全用二进制表示。例如,“1.015”印有几位小数,给出“1.01499999999999999”;因此,当四舍五入到两位十进制数字时,1.01 比 1.02 更接近真实值。其他示例也是如此。

还要记住,默认的 IEEE 754 舍入模式是“舍入到最近,并列到偶数”,这与“舍入到最近,从零开始”不同,后者可能是您在学校熟悉的。