Ruby 一些“BigDecimal”值不';与“Float”不匹配`

Ruby 一些“BigDecimal”值不';与“Float”不匹配`,ruby,rspec,floating-point,Ruby,Rspec,Floating Point,一些bigdecimic值可以通过Rspec3中的eq与Float进行比较,但一些值不能进行比较 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 为了避免错误,我使用了类似于eq(“83.79.To_d)的表达式

一些
bigdecimic
值可以通过Rspec3中的
eq
Float
进行比较,但一些值不能进行比较

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
为了避免错误,我使用了类似于
eq(“83.79.To_d)
的表达式


为什么第一个测试失败而第二个测试成功?

您不应该尝试任何类型的浮点值严格相等测试。您必须始终使用
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)> 
然后选择delta(
1e-12,本例中为
)以符合您的要求。

“83.79”。to_d在内部表示法中精确表示分数8379/100,因为它使用基数10(或其幂),而“83.79”。to_f不是因为内部表示法使用基数2,所以它们不相等

这与83.75不同,因为在基数2和基数10中都精确地表示了is(这是83+1/2+1/4)

如果在同一表达式中混合使用大小数和浮点,则浮点将转换为最接近的大小数。。。因此,您实际上是在执行以下操作:
83.79.to\u d
或换言之
“83.79.to\u f.to\u d


由于
“83.79.”to_f
不精确,而且由于大小数比浮点更精确,因此没有理由将其与
“83.79.”to_d
匹配

但是,如果您以另一种方式强制转换,我希望平等适用:

expect("83.79".to_d.to_f).to eq(83.79)

这是因为我们可以合理地预期(最不令人惊讶的是)到f的转换将回答精确分数最近的浮点,无论是从精确的大十进制还是字符串表示。

由于Ruby的大十进制和RSpec的存在,这不是一个标准问题的重复。“大十进制比浮点更精确”——事实上,有无限多的
BigDecimal
s,但只有2**64
float
s,因此
BigDecimal
s比
float
s多得多。
expect("83.79".to_d.to_f).to eq(83.79)