2016-10-05 34 views
2

關於期望由單獨計算生成的兩個浮點數完全相等的危險已經有很多問題和解答,因爲浮點數不是實數。這個問題是而不是關於正確性依賴於平等檢查,這是關於基於它的緩存。用於緩存昂貴計算的浮點平等

想象一下,你有這樣的代碼:

if(myfloat != _last_float) { 
    refresh_expensive_computation(myfloat); 
    _last_float = myfloat; 
} 

在這種情況下,相等比較純粹的存在是爲了避免做重複的工作。如果輸入保持不變,我們將避免再次進行昂貴的計算(我們假設昂貴的函數是確定性的,並且沒有其它輸入發生改變)。

如果兩者真的相等(意思是說,如果我們可以用實數而不是浮點來計算),但是錯誤地發現它們不是,最糟糕的情況是我們會冗餘地執行昂貴的計算,但是我們的程序的答案仍然是正確的。 AFAIK如果計算是在比浮點的存儲器表示更寬的寄存器中完成的(例如,在80位fp寄存器被啓用時在32位x86上),並且在轉換爲它們發生的存儲器表示之後,它們只能錯誤地比較相等都是按位平等的。在這種情況下,差異必須超出內存表示的精度,這必須低於對我來說很重要的比較,因爲否則我會使用更寬的類型,如double。

所以我要斷言浮點平等的使用是安全的。所以第一個問題是,我錯了嗎?其次,如果我們假設它是安全的,我想避免錯誤地返回true,因爲它會導致昂貴的計算。在寄存器比寄存器表示更寬的機器上避免這種情況的一種方法是使用memcmp來強制它比較內存表示(對於NaN,語義不會完全相同,現在將與NaN中的完全相同的按位實例本身,但是對於緩存來說這是一種改進,或者對於+0和-0來說,但是這可能是特殊的)。然而,memcmp將比寄存器中的浮點比較慢。有沒有一種方法可以檢測平臺何時具有更寬的寄存器,因此我可以#ifdef或類似的方式在安全的平臺上獲得優化的實現?

+0

你怎麼知道緩存的值是否正確,而無需進行計算來弄清楚它應該是什麼? – Dmitri

+1

對不起,緩存的float應該被稱爲last float,編輯得更清晰。我們看到輸入是否在變化。我們假設相同的輸入產生相同的輸出。 –

+0

好的...如果您要保存一個輸入/輸出對,並且在新輸入與保存的輸入相匹配時使用保存的輸出值,那麼只要給定輸入中只有一個輸出值有效,就應該沒問題。 ..雖然這似乎很明顯,所以我很驚訝你會問。 – Dmitri

回答

2

大多數memcmp實現具有寄存器的小值優化,所以它應該很好用。但是,如果你不想依賴那個,你也可以做類似reinterpret_cast<int>()的事情。如果您想要更安全並且正在使用包含此類命令的庫集,請添加compile_assert(sizeof(int) == sizeof(float))

不要小心NaN。 NaN不等於任何東西,甚至是另一個NaN。如果你比較這樣的內存,它們將顯示爲平等,這聽起來就像你想要的,但是你可能想要添加額外的代碼來確保所有的NaN都被視爲相同。

+0

有趣的是,沒有想到小的價值優化。在我的應用程序中,實際上成本似乎是調度到memcmp。我將不得不比較爲reinterpret強制生成的代碼。 –

0

(C99)爲了避免一些更高的精度FP從數學上設置未確切比較,使用volatile強迫計算使用最新的浮動值。

if ((volatile float) myfloat != (volatile float) _last_float) { 
    refresh_expensive_computation(myfloat); 
    _last_float = myfloat; 
} 

注意:在使用_作爲國內領先的字符,然後一個字母作爲變量名被保留。最好重命名爲_last_float

注:-0.0f等於+ 0.0f。如果這些具有相同值的不同float很重要,則需要其他代碼,而不是!=