21

我的理解是否正确,即使用 RubyBigDecimal类型(即使具有不同的精度和比例长度)应该准确计算,还是应该预料到浮点恶作剧?

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

4

1 回答 1

37

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

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

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

# bigdecimal
BigDecimal("1.0e+25") - BigDecimal("9999999999999999900000000.0")
#=> 100000000

1亿的精度差!很严重吧?

除了精度误差只有原始数字的 0.000000000000001% 左右。这真的是由你来决定这是否是一个问题。但是通过 using 可以解决问题,BigDecimal因为它具有任意精度。您唯一的限制是 Ruby 可用的内存。

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

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

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

# bigdecimal
(BigDecimal("0.29") * 50).round
#=> 15  # correct

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

同样,当您将数字四舍五入时,这会咬您一口。考虑:

# bigdecimal
(BigDecimal("1") / 9 * 9 / 2).round
#=> 0  # not correct

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

一些结论:

  • 如果您使用小数部分(例如金钱)进行计算,十进制浮点数非常棒。
  • 如果您需要任意精度的浮点,RubyBigDecimal也可以很好地工作,并且并不关心它们是十进制还是二进制浮点。
  • 如果您使用(科学)数据,您通常会处理固定精度的数字;Ruby 的内置浮点数可能就足够了。
  • 您永远不能期望任何类型的浮点算术在所有情况下都是精确的。
于 2010-06-14T20:56:13.880 回答