2017-10-15 58 views
0

在IRB:兩個非常接近的浮點數不相等

0.9999999999999998 == 1.0 
# => false 
0.99999999999999998 == 1.0 
# => true 

也正好顯示inspect值:

0.9999999999999998 
# => 0.9999999999999998 
0.99999999999999998 
# => 1.0 

在我的情況下,通過我的節目所創造的價值是不幸的是,第一個,所以我無法編寫一個將值驗證爲等於1的測試用例。

我下列選項之間作出決定:

  1. 在應用程序代碼中添加round電話,但是應用程序已經在工作,我只是我無法測試
  2. 在測試代碼
  3. 添加 round電話
  4. ???

你會推薦什麼方法?有沒有一種方法可以將我的程序配置爲在十進制後用十五個九進行0處理,等於1.0?這感覺有點令人沮喪,因爲小數點後十六個九點似乎是切斷 - 我只是一個短。

+1

您已經證明IRB將十進制浮點值解釋爲具有固定精度的類型Float,而不是類型爲具有任意精度的BigDecimal。這對你的程序做了什麼或者應該做什麼,或者說你的測試做了什麼或者應該做什麼都沒有提及。 –

+1

請注意,執行浮點數的精確(in)等同比較很少適合。使用任意精度數字並不會真的改變這一點。如果你想要具體的建議,那麼你需要明確說明你想要測試的謂詞是什麼。 –

+0

@JohnBollinger我想知道是否有一種方法,我可以配置浮點以'0.9999999999999998 == 1.0'的方式。就像我說的那樣,這個值(15個9)與我需要的精度(16個9)相比只有一個。我已經開始查看[Float](https://ruby-doc.org/core-2.2.3/Float.html)類中的常量 - 我嘗試更改'EPSILON'和'DIG',但它沒有沒有效果。 –

回答

3

閱讀這篇文章對如何小浮點差異比較:
http://c-faq.com/fp/fpequal.html

我轉換他們提出的解決方案,以紅寶石:

class Float 
    MY_EPSILON = 0.000000000000001 
    MY_TOLERANCE = 0.000000000000001 

    def eq_epsilon?(flt) 
    a = self 
    b = flt 

    (a - b).abs <= MY_EPSILON * a.abs 
    end 

    def self.reldif(a, b) 
    c = a.abs 
    d = b.abs 
    d = [c,d].max 

    d == 0.0 ? 0.0 : (a - b).abs/d 
    end 

    def eq_reldif?(flt) 
    Float.reldif(self, flt) <= MY_TOLERANCE 
    end 
end 

這樣,我們可以運行一些測試代碼:

f1 = 0.99999999999999998 
f2 = 0.9999999999999998 
f3 = 0.999999999999998 

[f1, f2, f3].each { |f| 
    p f.eq_epsilon?(1.0) 
    p 1.0.eq_epsilon?(f) 
} 

puts "--------------" 

[f1, f2, f3].each { |f| 
    p f.eq_reldif?(1.0) 
    p 1.0.eq_reldif?(f) 
} 

帶輸出:

true 
true 
true 
true 
false 
false 
-------------- 
true 
true 
true 
true 
false 
false 

但是可能需要更多的測試來確保它滿足您的所有要求。

相關問題