2014-01-30 27 views
3

this問題爲基礎,衆所周知,我們不應將等於比較運算應用於小數變量,由於數字錯誤(它不受編程語言限制):是圓形正確的方式進行浮點雙比較

bool CompareDoubles1 (double A, double B) 
{ 
    return A == B; 
} 

它是不正確的代碼。 我的問題是:

  1. 這是正確的兩個數字,然後比較?
  2. 它更有效率?

例如:

bool CompareDoubles1 (double A, double B) 
    { 
     double a = round(A,4); 
     double b = round(B,4) 
     return a == b; 
    } 

這是正確的?

編輯

我考慮輪是採取雙重(數字)的方法和INT(precition):我認爲

bool round (float number, int precision); 

編輯 是更好的主意我用這個比較方法表示的意思是:

bool CompareDoubles1 (double A, double B, int precision) 
     { 
      //precition could be the error expected when rounding 
      double a = round(A,precision); 
      double b = round(B,precision) 
      return a == b; 
     } 
+4

'round'的定義是什麼? – Erbureth

+0

比較浮點數的正確方法不是完全比較它們。 – Griwes

+1

第一個代碼是_is_正確的,即使比較很少是真的。如果要按位平等,則需要使用第一個代碼。關於使用「信任區域」或任何其他名稱的觀點是,在大多數情況下,您並不是指按位平等,而是「這兩個數字在某種精度下是否相同?」 – stefan

回答

5

通常,如果你真的有比較浮點值,你會指定一個公差:

bool CompareDoubles1 (double A, double B, double tolerance) 
{ 
    return std::abs(A - B) < tolerance; 
} 

選擇合適的公差將取決於價值和產生它們的計算的本質。

舍入不合適:兩個非常接近的值,您希望比較相等,可能會在不同的方向舍入並顯示不等。例如,當四捨五入到最接近的整數時,0.30.4會比較相等,但0.4999990.500001不會。

+0

或者你相對容差。或者你設計你的算法,以便對平等的測試是適當的;有很多情況下'=='可以在浮點上正確使用。 –

4

雙打一種常見的比較是實現爲

bool CompareDoubles2 (double A, double B) 
{ 
    return std::abs(A - B) < 1e-6; // small magic constant here 
} 

這顯然是不作爲檢查A == B高效,因爲它涉及多個步驟,即減法,調用的std :: ABS和最後用恆定的比較。

大約效率同樣的道理也適用於你提出的解決方案:

bool CompareDoubles1 (double A, double B) 
{ 
    double a = round(A,4); // the magic constant hides in the 4 
    double b = round(B,4); // and here again 
    return a == b; 
} 

同樣,這將不會像直接比較高效,但是 - 再 - 它甚至沒有嘗試做相同。

CompareDoubles2CompareDoubles1是否更快取決於您的機器和魔術常數的選擇。只要衡量它。您需要確保提供匹配的魔術常量,否則您將檢查與不同結果的不同信任區域的相等性。

+0

它不一定是魔法。實際上,我認爲它不應該是魔法,而應該有意識地選擇一個代表結果精確度的方法=)可以非常難以跟蹤令人驚奇的事情,即15位精確的0.000001和0.000002 *魔法*彼此相等。 – luk32

+0

@ luk32它在任意意義上都不是魔術。它確實需要慎重選擇。然而,我將它們稱爲「魔術常量」,因爲它們是硬編碼的常量,其目的不是直接明顯的,而應該用一個有意義的名稱替換。 (C.F.[關於魔法數字的維基百科](http://en.wikipedia.org/wiki/Magic_number_%28programming%29)) – stefan

+0

我知道你的意思。我認爲不應該在比較器中使用這樣的常量,而是將其作爲參數傳遞,甚至是可選的。這是一種不好的做法,可能會引起可怕的反彈。我只是說,這不應該是魔術。在你的魔力=)當然你的權利,當涉及到真正的問題,你*可以*做到這一點,它是否應該*是另一回事。我的意思是你沒有說,這個魔術實際上並不好。我認爲這值得明確指出。 – luk32

0

如果你的例子中使用的舍入函數意味着四捨五入到十進制數字,這是不正確的。例如,如果A和B分別爲0.000003和0.000004,則它們將被舍入爲0.0,因此將被比較爲相等。

一般用途的兼容函數不能使用常量容差,但使用相對容量函數。但這一切都在你引用你的問題的文章中解釋過。

+0

我不明白爲什麼0.000003和0.000004應該是一個例子,說明爲什麼四捨五入是一個壞主意。通常的| diff | stefan

+0

因爲它們相差33%,一般不應該相等。 Andreas H.的帖子也遵循同樣的論點。 –

0

與其他帖子類似,但引入了尺度不變:如果您正在做一些事情,比如將兩組數字加在一起,然後想知道兩組數字是否相等,您可以將對數比的絕對值(對數的差值)和測試,以確定它是否小於規定的容差。這樣,例如如果您在總和計算中將所有數字乘以10或100,則不會影響答案是否相等的結果。您應該有一個單獨的測試,以確定兩個數字是否相等,因爲它們足夠接近0.

1

我認爲將差異與固定容差進行比較是一個壞主意。

說出如果將公差設置爲1e-6會發生什麼,但是您比較的兩個數字是 1.11e-9和1.19e-9?

即使它們在第二位有效數字之後有所不同,它們也會被視爲相等。這可能不是你想要的。

我想一個更好的方式做比較

equal = (fabs(A - B) <= tol*max(fabs(A), fabs(B))) 

注意,< =(而不是<),因爲上面還必須爲0 == 0工作。如果您設置tol = 1e-14,則兩個數字在等於14位有效數字時將被視爲相等。

旁註:當你想測試一個數字是否爲零,那麼上述測試可能不理想,然後確實應該使用絕對閾值。