Ruby BigDecimal健全性检查(浮点newb)

And*_*ndy 20 ruby math floating-point ruby-on-rails bigdecimal

我的理解是正确的,使用Ruby BigDecimal类型(即使具有不同的精度和比例长度)应该准确计算还是应该预测浮点诡计?

我在Rails应用程序中的所有值都是BigDecimal类型,我看到一些错误(它们有不同的小数长度),希望它只是我的方法而不是我的对象类型.

mol*_*olf 33

使用浮点运算时有两个常见的陷阱.

第一个问题是Ruby浮点具有固定的精度.在实践中,这将是1)对你没有问题或2)灾难性的,或3)介于两者之间的东西.考虑以下:

# float
1.0e+25 - 9999999999999999900000000.0
#=> 0.0

# bigdecimal
BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0")
#=> 100000000
Run Code Online (Sandbox Code Playgroud)

精度差1亿!很严肃,对吗?

除精度误差仅为原始数的0.000000000000001%.你真的应该决定这是不是一个问题.但问题是通过使用删除,BigDecimal因为它具有任意精度.您唯一的限制是Ruby可用的内存.

第二个问题是浮点不能准确表达所有分数.特别是,它们有小数部分的问题,因为Ruby(和大多数其他语言)中的浮点数是二进制浮点数.例如,小数部分0.2是永久重复的二进制分数(0.001100110011...).无论精度如何,这都不能精确地存储在二进制浮点中.

当你对数字进行四舍五入时,这会产生很大的不同.考虑:

# float
(0.29 * 50).round
#=> 14  # not correct

# bigdecimal
(BigDecimal("0.29") * 50).round
#=> 15  # correct
Run Code Online (Sandbox Code Playgroud)

A BigDecimal可以精确地描述小数部分.但是,有一些分数无法用小数部分精确描述.例如,1/9是一个永久重复的小数部分(0.1111111111111...).

再次,当你围绕一个数字时,这会咬你.考虑:

# bigdecimal
(BigDecimal("1") / 9 * 9 / 2).round
#=> 0  # not correct
Run Code Online (Sandbox Code Playgroud)

在这种情况下,使用十进制浮点仍会产生舍入误差.

一些结论:

  • 如果你用十进制分数(例如金钱)进行计算,则十进制浮点数很棒.
  • BigDecimal如果你需要任意精度浮点数,Ruby 也可以正常运行,并且不关心它们是十进制还是二进制浮点数.
  • 如果你使用(科学)数据,你通常会处理固定的精确数字; Ruby的内置浮动可能就足够了.
  • 在任何情况下,您都不能指望任何类型的浮点运算都是精确的.

  • “在实践中,这要么是 1)对你来说没问题,要么是 2)灾难性的,或者 3)介于两者之间” - 喜欢你如何缩小范围:) (3认同)