提问人:Phil R 提问时间:7/21/2016 最后编辑:Mladen JablanovićPhil R 更新时间:7/21/2016 访问量:338
Ruby 的 BigDecimal 类中的浮点错误?[复制]
floating point error in Ruby's BigDecimal class? [duplicate]
问:
避免 ruby 中浮点错误的通用建议是使用 BigDecimal。我一定忽略了什么,因为我想我发现了一个 BigDecimal 数学返回错误的情况,而 Float 没有:
使用 Float 给出的正确答案是 2.75:
> 50.0 * 0.6 / 360.0 * 33
=> 2.75
使用 BigDecimal 给出了 2.74999999 的错误答案:
> BigDecimal("50") * BigDecimal("0.6") / BigDecimal("360") * BigDecimal("33")
=> #<BigDecimal:7efe74824c80,'0.2749999999 999999989E1',27(36)>
有人请告诉我我在这里错过了什么?
答:
3赞
Mladen Jablanović
7/21/2016
#1
让我们简化您的示例,并改用以下示例:
BigDecimal(1) / BigDecimal(3) * BigDecimal(3)
# => #<BigDecimal:19289d8,'0.9999999999 99999999E0',18(36)>
它是如何到达那里的?
BigDecimal(1) / BigDecimal(3)
# => #<BigDecimal:1921a70,'0.3333333333 33333333E0',18(36)>
BigDecimal 不提供有理数,所以当你把 1 除以 3 时,你会得到 0,后面跟着很多 3。很多,但不是无限多。当你把它乘以 3 时,你会得到 0,然后是同样多的 9。
我相信你误读了 BigDecimal 的广告(尽管我不确定它是否在任何地方被宣传为浮点错误的解决方案)。它只是提供任意精度。它仍然是一个浮点数。如果你真的想要在除数时精确的数字,你可以看看类:Rational
(Rational(50) * Rational(0.6) / Rational(360) * Rational(33)).to_f
# => 2.75
评论
0赞
cremno
7/21/2016
从 2.1 开始,可以使用后缀:。r
50r * Rational(0.6) / 360r * 33r
0赞
Phil R
7/21/2016
谢谢姆拉登。我正在编写一个金融应用程序,并考虑了以下所有类型来表示货币: * 整数,以美分为单位存储金额:不好,因为我们需要能够存储一美分的分数。* Money gem:使用整数,所以和整数一样的问题。* 浮点数:容易出现浮点错误。* BigDecimal:不太容易出现浮点错误(但现在我发现,在我们的一个用例中仍然容易出错!也许理性就是答案。
1赞
Phil R
7/21/2016
另一个似乎对我有用的解决方案: BigDecimal(“50”) * BigDecimal(“0.6”) * BigDecimal(“33”) / BigDecimal(“360”) 这是基于线程中的一条评论,Jordan 将其标记为重复项: 实际上,准确进行此类计算的关键不是使用定点算术,而是安排计算以避免必须对将要放大的值进行四舍五入。例如,要向某人收取 30 天月中 17 天的费用,请将每月金额乘以 17,然后除以 30,而不是除以 30 再乘以 17。– 超级猫 Oct 1 '14 at 15:13
评论
BigDecimal("50") * BigDecimal("0.6") * BigDecimal("33") / BigDecimal("360")
990 / 360
(11 * 90) / (4 * 90)