代码之家  ›  专栏  ›  技术社区  ›  Amin Shah Gilani

为什么'9.3==9.3.to`d`false?

  •  0
  • Amin Shah Gilani  · 技术社区  · 6 年前

    我在TDD期间遇到了一个有趣的案例:

     Failure/Error: expect(MoneyManager::CustomsCalculator.call(price: 31,    weight: 1.12)).to    eq 9.3
    
       expected: 9.3
            got: 0.93e1
    

    我进一步调查发现:

    require 'bigdecimal'
     => true
    2.4.2 :005 > require 'bigdecimal/util'
     => true
    ...
    2.4.2 :008 > 1 == 1.to_d
     => true
    2.4.2 :009 > 2 == 2.to_d
     => true
    2.4.2 :010 > 2.0 == 2.0.to_d
     => true
    2.4.2 :011 > 1.3 == 1.3.to_d
     => true
    2.4.2 :012 > 9.3 == 9.3.to_d
     => false
    

    为什么 9.3 == 9.3.to_d false ?

    另外,我很清楚Float和BigDecimal是什么,但我对这种特殊的行为感到很困惑。

    2 回复  |  直到 6 年前
        1
  •  3
  •   Tom Lord    6 年前

    这并不是真正的“ruby问题”。这是一个 数字的浮点表示法 问题。

    你不能 可靠的 在浮点数和“精确”值之间执行相等检查(由 BigDecimal ).

    BigDecimal.new(9.3, 2) 是准确的。 9.3 不是。

    9.3 * 100 #=> 930.0000000000001
    1.3 * 100 #=> 130.0
    

    这就是二进制浮点数的工作原理。它们(有时)是“真实”价值的不精确表示。

    您可以:

    • 以貌取人( bigdecimal1 == bigdecimal2 ,或 float1 == float2 ). 但也要注意比较 浮动1==浮动2 也不可靠,如果你执行不同的计算,以获得这些值!!或者,
    • 检查值是否相等 在错误范围内 (例如,在 rspec 条款, expect(value1).to be_within(1e-12).of(value2) ).
        2
  •  1
  •   tukan    6 年前

    根据上面的Eric评论编辑

    你可以使用float的性质,并将其与你提议的一个限制进行比较,这个限制将返回 true false 可靠。

    (bigdecimal-float).abs < comparison_limit
    

    在您的示例中(我添加了()以提高可读性):

    ((9.3.to_d)-9.3).abs < 0.000001  <-- watch out for the limit!
    

    会产生 真的 可用于测试。

    编辑 基于埃里克(谢谢)的评论。 比较这两个数字时,务必检查公差的极限。

    你可以这样做:

    9.3.next_float
    

    它会给你

    9.300000000000002
    

    所以你的容忍度应该是

    0.000000000000002
    

    注意:注意台阶:

    9.3.next_float.next_float
    => 9.300000000000004
    

    现在代码看起来不同了:

    ((9.3.to_d)-9.3).abs < 0.000000000000002