2016-03-08 62 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 。 –

相關問題