2015-04-27 23 views
1

從N3337(C++ 11草案)節3.9.1.8:這與浮點數字零有效的比較?

浮點類型的值表示是實現定義的。

這是否適用於浮點類型的任何和所有用法,無論它是否是文字?這是導致我有些擔憂的例子:

float foo{0.0f}; 
if (foo == 0.0f) 
{ 
    // Am I always guaranteed to get here? 
} 

如果我們假設0.0f是不是真的0至於執行方面,但是一些不確定的數量,將這個比較還是在技術上是因爲這兩種有效操作數是通過常量獲得的,儘管我可能不知道它的真實值,但它們仍然是相同的?

像這樣的浮點文字的平等比較總是有代碼味道,我只是想確保沒有某些使用情況,這是有道理的或有效的。

+2

注意_integer_類型和_pointers_的值表示也是實現定義的。而且我們肯定會期望這對整數有效,指針不是嗎? –

+2

是的。但是,浮點運算會因舍入錯誤而受到影響,因此在浮點運算完成後您將無法得到保證。 – phantom

+0

個名堂浮動(0)等於任何浮點/整數0無論implementaion –

回答

1

是的,你保證到達那裏。對有關數字進行處理操作後發生浮點不精確。常量在你的情況下是安全的。

但是,如果通過提供過多的小數來超出浮點數精度,或者您使用另一種數據類型初始化浮點數,它可能會得到不同的解釋。

舉例來說,這可能不是讓它:

float foo{2.1234321f}; 
if (foo * 6.1234321f/0.1234321f == 105.3428750f) 
{ 
    // Am I always guaranteed to get here? Not at all. 
} 

如果你想比較浮點數時,爲了安全起見,你應該「近似」的結果。請參閱下面的代碼。

#include <limits> 
#include <type_traits> 

using namespace std; 

class exact{}; 
class approx{}; 

template<class> struct tolerance; 

template<> 
struct tolerance<float> 
{ 
    static constexpr float value() { return 0.00001; } 
} 

template<class T> 
bool close_enough(T a, T b, exact) 
{ 
    return a == b; 
} 

template<class T> 
bool close_enough(T a, T b, approx) 
{ 
    return abs(a - b) <= tolerance<T>::value(); 
} 

template<class T> 
bool close_enough(T a, T b) 
{ 
    return close_enough(a, b, 
     conditional<numeric_limits<T>::is_exact, exact, approx>::type{}); 
} 

int main() 
{ 
    float a = 2.1234321f, b = 105.3428750f; 

    if (close_enough(a * 6.1234321f/0.1234321f, b)) 
    { 
     // Am I always guaranteed to get here? Yes! 
    } 
    else 
    { 
     // ... 
    } 
} 
+4

我不確定「常量是否安全」。如果'float'被初始化爲文字「0.1」,然後與相同的文字進行比較,則會失敗。這取決於你如何在這裏定義「常量」。 –

+0

不應該是'if(foo * 24.0f/0.5f == 24.0f)'或'if(foo * 6.0f * 2.0f == 24.0f)'嗎? – NathanOliver

+1

此外,實現一個接受兩個浮點數值之間公差/距離的函數通常是一個好主意,並且在比較兩個不是常量的浮點數或者浮點數有大量有效數字後使用該函數小數點。 –

0

據我所知,它是保證去那個街區。由於0.0f是一個浮點常數。

float f = 0.0; /* OK, throw away bits to convert 0.0 from double to float */ 
assert (f == 0.0); /* not OK, f is converted from float to double 
    and the value of 0.0 depends on how many bits you use to represent it. */ 
assert (f == 0.0f); /* OK, comparing two floats, although == is finicky. */ 

還要注意的是,在float x = 0有從int隱式類型轉換浮動。 而在float x = 0.0f我們沒有這樣的類型轉換。在float x = 0.0中,我們有一個從double到float的隱式類型轉換。

很好看的:what every computer scientist should know about floating-point arithmetic

+0

當你說「0.0的值取決於你用來表示它的位數」時,你出錯了。雖然這對許多值可能是正確的,但0.0可以精確地表示。 (所以'1.0') –

+0

實際上,兩個斷言都不完全正確:理論上,float float f = 0.0;中的double常量的轉換可能會改變這個值。因此,將它轉換回來並將其與原始常量進行比較既不可行,也不可能將它與float類型的常量進行比較,因爲它可能已經以不同方式舍入。但是鏈接是好的:-) – cmaster

-1

在實踐

float foo = 0.0 ; 
if (foo == 0.0) 
{ 
} 

往往工作。但是,做

float foot = SOMEARBITRARYCONSTANT ; 
if (foo == SOMEARBITRARYCONSTANT) 
{ 
} 

可能無法正常工作。

當SOMEARBITRARYCONSTANT是一個可以精確表示的值並且否則可能會失敗時,它往往會工作。

我見過相當於:

float foo = 0.1 ; 
if (foo == 0.1) 
{ 
} 

無法工作,因爲編譯器四捨五入0.1不同。

一般來說,浮點==是一個壞主意。

+1

'foo == 0.1' **必須**將系統的計算結果設爲false,該系統將'float'映射到IEEE 754 binary32並將'double'映射到binary64,因爲然後'(float)0.1' ,'foo'的值不同於'0.1'的值'0.1'。 –

0

雖然在技術上處於約浮點數,CPU上只能使用其中兩種不同的表示標準很少保證:二進制編碼的十進制,並IEEE 754。現在第一個是比較深奧的,並且從來沒有默認使用過,因爲它比IEEE浮點數提供的精度要低,所以假設你有浮點數時就有IEEE數字是安全的。

爲什麼這很重要?簡單地說,因爲IEEE格式保證了很多數值的精確性。具體而言,它定義了什麼+0.0-0.0是,它從格式規範,一個float可以在範圍內-16777216表示所有整數16777216準確如下。 (同樣,double格式允許的範圍內-2^532^53,這是相當舒適的。)從這個,你可以讓你的計算停留多久準確扣除,因此允許相等比較。

然而,只要您使用這些信息,你也應該寫您的扣除註釋,你不能想當然地認爲別人會發現他們明顯...