2011-07-17 43 views
15

可能重複:
strange output in comparision of float with float literal爲什麼比較double和float會導致意想不到的結果?

float f = 1.1; 
double d = 1.1; 
if(f == d) // returns false! 

爲什麼會這樣呢?

+0

http://www.boost.org/doc/libs/1_34_1/libs/test/doc/components/test_tools/floating_point_comparison.html – darlinton

+11

谷歌「每個計算機科學家應該知道的有關浮點運算的內容」 。 – Bart

+2

切勿使用'=='來比較浮點值。相反,使用更多沿着'if(abs(f - d)<0.001)'行的東西。 – aroth

回答

30

考慮的重要因素,與floatdouble號碼爲:
精密 & 四捨五入


精密:
浮點數的精度是多少位它可以表示而不會丟失它包含的任何信息。

考慮分數1/3。這個數字的十進制表示形式是0.33333333333333…,其中3的數字是無限的。無限長度的數字將需要精確描述無限存儲器,但floatdouble數據類型通常只有48字節。因此浮點數爲&的雙數只能存儲一定數量的數字,其餘的則必然會丟失。因此,沒有確切的方法用浮點數或雙精度數表示需要比變量可以保持更高精度的數字。


四捨五入:
binarydecimal (base 10)數之間的非顯着性差異。
考慮分數1/10。在decimal中,這可以很容易地表示爲0.1,並且0.1可以被認爲是容易表示的數字。 0.00011001100110011…

一個例子:

#include <iomanip> 
int main() 
{ 
    using namespace std; 
    cout << setprecision(17); 
    double dValue = 0.1; 
    cout << dValue << endl; 
} 

該輸出是:

0.10000000000000001 

而且不

0.1. 

這是然而,在二進制,0.1由無限序列表示因爲由於它的有限的記憶,雙重必須截斷近似值y,這會導致一個數字不完全是0.1。這種情況稱爲舍入錯誤


當比較兩個密切float和double數字,例如舍入誤差踢,最終比較產生不正確的結果,這是你永遠不應該比較浮點數或使用==雙的原因。

你可以做的最好的是採取他們的區別,並檢查它是否小於一個epsilon。

abs(x - y) < epsilon 
+0

這是一個有效的答案,用於抵消downvotes的+1。 它留下了所需的信息,但都是一樣的。 –

+1

他們可以相當準確地表示,只是不是絕對精確地在所有情況下。 – aroth

+1

@Als:這個信息好多了。將我的倒票轉換爲upvote,並刪除了我的反對意見:) –

2

的IEEE 754 32位float可以存儲:1.1000000238...
的IEEE 754 64位double可以存儲:1.1000000000000000888...

瞭解爲什麼他們不是 「平等」?


在IEEE 754,分數都存儲在2的冪:

2^(-1), 2^(-2), 2^(-3), ... 
1/2, 1/4, 1/8, ... 

現在,我們需要一種方法來表示0.1。這是(的簡化版本)32位IEEE 754表示(浮點):

2^(-4) + 2^(-5) + 2^(-8) + 2^(-9) + 2^(-12) + 2^(-13) + ... + 2^(-24) + 2^(-25) + 2^(-27) 
00011001100110011001101 
1.10000002384185791015625 

隨着64位double,它更準確。它不停止在2^(-25),它保持大約兩倍。 (2^(-48) + 2^(-49) + 2^(-51),也許?)


資源

IEEE 754 Converter(32位)

0

花車和雙打都存儲在不能代表每一個數字完全是一個二進制格式(在有限空間中無法表示無限多個可能的不同數字)。

因此他們做舍入。浮點數必須翻一倍以上,因爲它比較小,所以1.1舍入到最近的有效浮點數不同於1.1舍入到最接近的valud Double。

要看到什麼號碼是有效的花車和雙打看到Floating Point

4

一般來說,你不應該浮動比較花車,雙打雙打,或漂浮雙打使用==

最好的做法是將它們相減,並檢查差異的絕對值是否小於一個小的epsilon。

if(std::fabs(f - d) < std::numeric_limits<float>::epsilon()) 
{ 
    // ... 
} 

的一個原因是因爲浮點數是(或多或少)二進制小數,並且只能近似許多十進制數。許多十進制數字必須轉換爲重複的二進制「小數」或無理數。這會引入舍入錯誤。

From wikipedia

例如,1/5不能精確表示爲使用二進制鹼浮點數但可以準確地使用十進制基數來表示。

在您的具體情況下,浮點數和double將對用於表示1.1二進制數的無理數/重複小數有不同的舍入。在相應的轉換引入不同級別的舍入誤差後,您將很難讓它們「平等」。

上面給出的代碼通過簡單地檢查這些值是否在很短的三角洲內解決了這個問題。你的比較變化是「這些值是否相等?」到「這些值是否在相互之間的小誤差範圍內?」

而且,看到了這個問題:What is the most effective way for float and double comparison?

也有很多關於那個飄打破了簡單的相等比較點數其他古怪的。檢查這篇文章的一些人的描述:

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

+0

ε鄰居通常不是最佳做法,而是一種懶惰的解決方法。大多數情況下,你真的不需要'==',但是'<=' or '> =',只是認爲你需要'==',因爲這種情況對於整數來說工作得很好,因爲'''永遠不會被這些情況達到。 - 另外,有時你實際上需要'=='浮點數,儘管這很少發生。 – leftaroundabout

+0

@leftaroundabout:這與我讀過的大多數文章相反。你能詳細說明這些情況嗎?也許添加你自己的答案:) –

7

試着運行這段代碼,結果會使原因顯而易見。

#include <iomanip> 
#include <iostream> 

int main() 
{ 
    std::cout << std::setprecision(100) << (double)1.1 << std::endl; 
    std::cout << std::setprecision(100) << (float)1.1 << std::endl; 
    std::cout << std::setprecision(100) << (double)((float)1.1) << std::endl; 
} 

輸出:

1.100000000000000088817841970012523233890533447265625 
1.10000002384185791015625 
1.10000002384185791015625 

既不float也不double可以代表1.1準確。當您嘗試進行比較時,浮點數被隱式上轉換爲double。雙數據類型可以準確地表示浮點數的內容,因此比較結果爲false。

相關問題