ruby-on-rails - Ruby BigDecimal 健全性检查( float newb)

标签 ruby-on-rails ruby math floating-point bigdecimal

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

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

最佳答案

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

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

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

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

一亿精度的差距!很严重,对吧?

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

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

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

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

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

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

同样,当你四舍五入一个数字时,这会咬你。考虑:

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

在这种情况下,使用小数 float 会产生舍入误差。

一些结论:

  • 如果您使用小数进行计算(例如金钱),小数 float 非常棒。
  • Ruby 的 BigDecimal 如果您需要任意精度的 float 并且并不关心它们是十进制 float 还是二进制 float ,它也能很好地工作。
  • 如果您处理(科学)数据,您通常会处理固定精度的数字; Ruby 的内置 float 可能就足够了。
  • 您永远不能期望使用任何种 float 的算术在所有情况下都是精确的。

关于ruby-on-rails - Ruby BigDecimal 健全性检查( float newb),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3039650/

相关文章:

ruby-on-rails - 在Tire和Ruby on Rails上使用Elasticsearch搜索电子邮件时结果无效

ruby-on-rails - RoR路由错误

ruby - 在 ruby​​ 中使用正确的异常子类

math - float 学坏了吗?

java - 奇怪的 Java 数学结果

ruby-on-rails - 无法让 Ajax 处理关注/取消关注按钮 - 遵循 Railstutorial.org,第 11 课

ruby-on-rails - 在子文件夹中分组的部分

javascript - 如何扭曲一组随机数的分布?

ruby-on-rails - 比较 bigdecimal 输出

ruby-on-rails - 只有当所有其他验证都通过时才执行一个验证