【问题标题】:Ruby BigDecimal sanity check (floating point newb)Ruby BigDecimal 健全性检查(浮点 newb)
【发布时间】:2011-03-03 15:37:03
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

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

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

    # 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
    

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

    一些结论:

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

    【讨论】:

    • “在实践中,这要么是 1) 对您没有问题,要么 2) 灾难性的,或者 3) 介于两者之间” - 喜欢你缩小范围的方式:)
    猜你喜欢
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-30
    • 2020-12-24
    • 2015-04-10
    相关资源
    最近更新 更多