某些BigDecimal
值可以与Rspec3中Float
的{{1}}进行比较,但某些值不能。
eq
为避免错误,我使用describe "compare BigDecimal with Float" do
it { expect("83.79".to_d).to eq(83.79) } # => fail
it { expect("83.75".to_d).to eq(83.75) } # => succeed
end
等表达式。
为什么第一次测试失败而第二次测试成功?
答案 0 :(得分:4)
您永远不应该尝试使用浮点值进行任何类型的严格相等测试。您始终必须使用Float
处理不准确的内部代表问题,因此==
和!=
并不十分有用。
考虑一下:
'83.79'.to_d - 83.79
# => #<BigDecimal:7ff33fcea560,'-0.1E-13',9(36)>
'83.75'.to_d - 83.75
# => #<BigDecimal:7ff33fcee688,'0.0',9(27)>
请注意83.79
的差异并非完全为零。
如果需要比较浮点值,则总是需要在比较中使用delta;你总想说:
这些值是否相互之间有少量差异?
而不是
这些值是否相等?
在Rspec术语中:
expect('83.75'.to_d).to be_within(1e-12).of(83.75)
expect('83.79'.to_d).to be_within(1e-12).of(83.79)
并选择delta(在这种情况下为1e-12
)以符合您的要求。
答案 1 :(得分:2)
&#34; 83.79&#34; .to_d正好代表内部表示中的分数8379/100,因为它使用基数10(或其幂),而#34; 83.79&#34;。 to_f不是因为内部表示使用基数2,所以它们不相等。
对于83.75来说,这是不一样的,因为is在两个基数2和10中都是完全相同的(这是83 + 1/2 + 1/4)。
如果在同一个表达式中混合使用大小数和浮点数,则浮点值将转换为最接近的大小数点...因此,您实际上是执行此操作:83.79.to_d
或以不同方式"83.79".to_f.to_d
<登记/>
由于"83.79".to_f
不准确,并且因为大小数比浮点数更准确,所以没有理由它与"83.79".to_d
匹配。
但是,如果你以另一种方式强制转换,我希望平等成立:
expect("83.79".to_d.to_f).to eq(83.79)
这是因为我们可以合理地期望(最不惊讶)转换to_f将回答最接近精确分数的浮点,无论是精确的大小数还是字符串表示。