2016-03-08 63 views
1

某些BigDecimal值可以与Rspec3中的eqFloat比较,但有些值不能。某些`BigDecimal`值与`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)

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

+0

这不是规范[“浮点已损坏”](http:// stackoverflow。com/q/588004/479863)由于Ruby的BigDecimal和RSpec的存在而引发的问题。 –

回答

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差别不大为零。

如果您需要比较浮点值,您总是需要在比较中使用增量;你总是想说:

这些数值是否相距很小?

而不是

这些是相等的值?

在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) 

,并选择增量(1e-12在这种情况下),以满足您的要求。

2

“83.79”.to_d正好在内部表示中表示分数8379/100,因为它使用基数10(或其权力),而“83.79”.to_f不是因为内部表示使用基数2,所以这些都不一样。

这与83.75不一样,因为在基数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) 

这是因为我们可以reasonnably预期(最小惊讶),与转换to_f会回答最接近的浮点数确切的分数,无论是从一个确切的大数字或字符串表示。

+0

“十进制数比浮点数更精确” - 事实上,无限多的'BigDecimal's只有2 ** 64'Float's,所以无限多的'BigDecimal's比'Float's 。 –

相关问题