我在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
?
PS,我很清楚Float和BigDecimal是什么,但是我对这种特殊的行为感到困惑。
答案 0 :(得分:3)
这并不是一个真正的“红宝石问题”。这是数字的浮点表示形式问题。
您不能可靠地执行浮点数与“精确”值(由BigDecimal
表示)之间的相等性检查。
BigDecimal.new(9.3, 2)
是正确的。 9.3
不是。
9.3 * 100 #=> 930.0000000000001
1.3 * 100 #=> 130.0
这就是二进制浮点数的工作方式。它们(有时)是“真”值的不精确表示。
您可以:
bigdecimal1 == bigdecimal2
或float1 == float2
)。但也请注意,如果您执行不同的计算以获得这些值,则比较float1 == float2
也是不可靠的!或者,rspec
的术语,expect(value1).to be_within(1e-12).of(value2)
)。答案 1 :(得分:1)
由于上述Eric评论而进行了编辑
您可以使用float的性质并将其与您建议的限制进行比较,该限制将可靠地返回stratify
或true
。
false
在您的示例中,这是(我添加了()以提高可读性):
(bigdecimal-float).abs < comparison_limit
哪个产生((9.3.to_d)-9.3).abs < 0.000001 <-- watch out for the limit!
并可以用于测试。
编辑基于Eric的评论(谢谢您)。 比较两个数字时,务必检查公差极限。
您可以通过以下方式进行操作:
true
这会给你
9.3.next_float
所以你的容忍度应该是
9.300000000000002
注意:注意步骤:
0.000000000000002
现在代码看起来不同:
9.3.next_float.next_float
=> 9.300000000000004