2017-04-10 126 views
1

今天在我的C++編程課程中,我的proff告訴我,不應該直接比較兩個浮點值。浮點運算

所以我試了這段代碼,並找出他的說法的原因。

double l_Value=94.9; 
print("%.20lf",l_Value); 

,我發現結果94.89999999(一些相對誤差)

據我所知,浮點數並不存儲在一個呈現給代碼的方式。以二進制形式壓縮那些零和零涉及一些相對舍入誤差。

Iam尋找兩個問題的解決方案。 1.有效比較兩個浮點值的方法。 2.如何將浮動值添加到另一個。例。加0.1111到94.4345得到的確切值爲94.5456

在此先感謝。

+2

閱讀[這篇文章](http://stackoverflow.com/questions/588004/is-floating-point-math-broken)。 –

+0

「不應該直接比較兩個浮點值。」如果你的教授這麼說,找一位新教授。他應該說的是從不直接檢查** equal **的兩個浮點值。比較它們以確定一個是大於還是小於另一個是沒有問題的。 – JeremyP

+0

我忘了,他確實說過不應該進行平等檢查。 – AstroMax

回答

1
  1. 有效的方式來比較兩個浮點值。

一個簡單double a,b; if (a == b)是比較兩個浮點值的有效方法。然而,正如OP注意到的,這可能不符合整體編碼目標。更好的方法取決於比較的上下文,OP沒有提供。見下面。

  1. 如何向另一個浮動值添加浮動值。例。加入到0.1111 94.4345得到確切的價值94.5456

浮動值作爲源代碼具有無限的有效範圍和精度,如1.23456789e1234567。將該文本轉換爲double通常限於不同的值中的一個。選擇最接近的,但可能不完全匹配。

作爲典型的double0.1111, 94.4345, 94.5456都不能代表正好

OP具有選擇:

1)使用比double, float等其他類型。各種庫提供十進制浮點類型。

2)限制代碼到罕見​​的平臺,支持double到基地10的形式,如FLT_RADIX == 10

3)編寫自己的代碼來處理像"0.1111"這樣的用戶輸入到結構/字符串中並執行所需的操作。

4)將用戶輸入視爲字符串並將其轉換爲某種整數類型,再次使用支持的例程讀取/計算/寫入。

5)接受浮點運算在數學上不精確並處理舍入誤差。

double a = 0.1111; 
printf("a: %.*e\n", DBL_DECIMAL_DIG -1 , a); 
double b = 94.4345; 
printf("b: %.*e\n", DBL_DECIMAL_DIG -1 , b); 
double sum = a + b; 
printf("sum: %.*e\n", DBL_DECIMAL_DIG -1 , sum); 
printf("%.4f\n", sum); 

輸出

a: 1.1110000000000000e-01 
b: 9.4434500000000000e+01 
sum: 9.4545599999999993e+01 
94.5456 // Desired textual output based on a rounded `sum` to the nearest 0.0001 

更多關於#1

如果確切比較不尋求而是某種「有足夠的兩個值接近?「,需要」足夠接近「的定義 - 其中有很多。

以下「足夠接近」通過檢查這兩個數字的ULP來比較距離。當這些值處於相同的冪乘,並且其他方式變爲對數時,它是線性差異。當然,改變標誌是一個問題。

float例如:
考慮所有有限float從最負到最正有序。以下稍微可移植的代碼將爲每個float返回一個整數,其中的順序爲

uint32_t sequence_f(float x) { 
    union { 
    float f; 
    uint32_t u32; 
    } u; 
    assert(sizeof(float) == sizeof(uint32_t)); 
    u.f = x; 
    if (u.u32 & 0x80000000) { 
    u.u32 ^= 0x80000000; 
    return 0x80000000 - u.u32; 
    } 
    return u.u3 
} 

現在,爲了確定兩個float是「足夠接近」,簡單的比較兩個整數。

static bool close_enough(float x, float y, uint32_t ULP_delta) { 
    uint32_t ullx = sequence_f(x); 
    uint32_t ully = sequence_f(y); 
    if (ullx > ully) return (ullx - ully) <= ULP_delta; 
    return (ully - ullx) <= ULP_delta; 
} 
0

我通常這樣做的方式是有一個自定義的相等比較函數。基本的想法是,你有一定的容忍度,比如0.0001或者什麼的。然後你減去你的兩個數字並取其絕對值,如果它小於你的容忍度,你把它看作是平等的。當然,還有其他策略可能更適合某些情況。

+1

檢查閾值的問題在於,epsilon的選擇始終是任意的。問問你自己,如果你有一個很好的理由,你需要首先檢查平等。 – nucleon

0
  1. 定義自己公差等級e(例如,e=.0001),並檢查是否abs(a-b) <= e

  2. 你不會得到與浮點「精確」的價值。永遠。如果您事先知道您使用的是四位小數,並且您想要「確切」,那麼您需要將您的數字作爲整數內部處理,並只將它們顯示爲小數。 944345 + 1111 = 945456

+2

「你不會得到一個具有浮點數的」確切「值,永遠。」是的,如果你的值是一個整數或分數部分的分母是2的冪。 – JeremyP

+0

這是浮點數範圍的非常小的子集。但是,如果你想依靠這一點,就要走向前進。 – hymie