- 編寫高質(zhì)量代碼:改善C程序代碼的125個建議
- 馬偉 著
- 1338字
- 2019-01-01 01:33:11
建議3-4:避免直接在浮點數(shù)中使用“==”操作符做相等判斷
在整型數(shù)據(jù)中,我們一般都使用“==”操作符來判斷兩個數(shù)是否相等。在浮點數(shù)據(jù)的運算中,也存在著“==”操作符,那么是否也可以使用這個“==”操作符來判斷兩個浮點數(shù)是否相等呢?帶著這個問題,示例程序如代碼清單1-21所示。
代碼清單1-21 浮點數(shù)相等判斷示例
#include <stdio.h> int main(void) { float f1=3.46f; float f2=5.77f; float f3=9.23f; printf("f1(3.46f)=%0.20f\nf2(5.77f)=%0.20f\nf1+f2=%0.20f\n f3(9.23f)=%0.20f\n",f1,f2,f1+f2,f3); if(f1+f2==f3) { printf("f1+f2==f3\n"); } else { printf("f1+f2!=f3\n"); } return 0; }
在代碼清單1-21中,分別定義了3個float變量f1、f2與f3。從表面上看,f1+f2的值應(yīng)該是9.23,因此執(zhí)行條件判斷語句“if(f1+f2==f3)”時,應(yīng)該返回true。但實際的運行結(jié)果并非如此,如圖1-33所示。
要想使語句“if(f1+f2==f3)”返回true,那么f1+f2和f3在浮點格式的精度限制內(nèi)必須嚴(yán)格相等。這也就意味著,一般情形下(0除外),浮點格式中的每一個位都必須相等。

圖1-33 代碼清單1-21的運行結(jié)果
由于浮點數(shù)存在誤差,即使是同一意義上的值,如果來源不同,那么判斷也就可能不會為true。換句話說,在浮點計算中,“==”的作用是比較兩個浮點數(shù)是否具有完全相同的格式數(shù)據(jù),而不是一般數(shù)學(xué)或工程意義上的相等。在浮點計算中兩個數(shù)據(jù)相等的含義通常是指在誤差范圍內(nèi),兩個數(shù)據(jù)的意義一致(即二者描述的物理量的取值一致,或者說相容),因此不能使用“==”操作符進(jìn)行判斷。
既然不能使用“==”操作符進(jìn)行判斷,那么我們又應(yīng)該怎樣正確判斷兩個浮點數(shù)是否相等呢?
一般情況下,浮點數(shù)的相等判斷通常使用如下形式,即:

示例代碼如下所示:
if(fabs(a-b) < epsilon)
其中,epsilon是一個絕對的數(shù)據(jù)。采用這種形式來判斷相等,很顯然,如何確定Δ就成了問題的關(guān)鍵所在。Δ值的確定需要考慮數(shù)值背后的含義,而且它總是與誤差的概念相隨。
(1)依據(jù)數(shù)據(jù)誤差進(jìn)行判斷
如果兩個數(shù)據(jù)相差Δ,假設(shè)一個數(shù)據(jù)的誤差是Δ1,另一個數(shù)據(jù)的誤差是Δ2,那么一個簡單的判據(jù)是:

實際上,如果數(shù)據(jù)不是直接來自某個測量設(shè)備,而是某個仿真系統(tǒng)的輸出或者是測量數(shù)據(jù)經(jīng)過一系列處理的結(jié)果,那么Δ1和Δ2大多沒有確定的值。此外,這種方法在理論上也不夠嚴(yán)謹(jǐn),只是便于使用而已。
(2)依據(jù)允許誤差進(jìn)行判斷
在許多情況下,計算精度和數(shù)據(jù)精度均遠(yuǎn)遠(yuǎn)超過了實際需求,使用數(shù)據(jù)誤差進(jìn)行相等判斷除了加大計算量之外,沒有實際意義。此時,則可根據(jù)實際精度需求確定允許誤差,然后用允許誤差替代數(shù)據(jù)誤差進(jìn)行相等判斷。這種方法更簡單,而且允許誤差一般遠(yuǎn)大于數(shù)據(jù)誤差,可以減小計算量。不過,所謂的允許誤差往往沒有確定的值,主要依據(jù)經(jīng)驗來判斷,因此有較大的不確定性。
雖然相對于“==”操作符,使用if(fabs(a-b)<epsilon)形式進(jìn)行判斷是一個比較好的解決方案,但它卻存在著一定的局限性。比如,epsilon的取值為0.0001,而a和b的數(shù)值大小也在0.0001附近,那么它顯然是不合適的。另外,對于a和b大小是10000這樣的數(shù)據(jù),它也不合適,因為10000和10001也可以認(rèn)為是相等的。
既然這種絕對誤差形式“if(fabs(a-b)<epsilon)”存在著局限性,那么我們可以嘗試使用相對誤差的形式“fabs((a-b)/a)<epsilon”進(jìn)行判斷,示例代碼如下所示:
bool IsEqual(float a, float b, float epsilon ) { return ( fabs ( (a-b)/a ) < epsilon ) ? true : false; }
這樣的判斷形式看起來是可行的,但它同樣存在著局限性。因為它是拿固定的第一個參數(shù)做比較的,如果我們分別調(diào)用IsEqual(a,b,epsilon)和IsEqual(b,a,epsilon),那么可能會得到不同的結(jié)果。與此同時,如果第一個參數(shù)是0,很可能會產(chǎn)生除0溢出。因此,我們可以把上面的判斷形式改造為:除數(shù)選取為a和b當(dāng)中絕對數(shù)值較大的即可,示例代碼如下所示:
bool IsEqual(float a, float b, float epsilon ) { if (fabs(a)>fabs(b)) { return ( fabs((a-b)/a) < epsilon ) ? true : false; } else { return (fabs( (a-b)/b) < epsilon ) ? true : false; } }
這樣看起來就更加完善了。當(dāng)然,在某些特殊的情況下,相對誤差也不能代表全部。因此,我們還需要將相對誤差和絕對誤差結(jié)合使用。完整的比較示例代碼如下所示:
bool IsEqual(float a, float b, float epsilon ) { if (a==b) { return true; } if (fabs(a-b)<epsilon ) { return true; } if (fabs(a)>fabs(b)) { return ( fabs((a-b)/a) < epsilon ) ? true : false; } else { return (fabs( (a-b)/b) < epsilon ) ? true : false; } }
- 數(shù)據(jù)庫系統(tǒng)原理及MySQL應(yīng)用教程(第2版)
- Instant Testing with CasperJS
- Java EE 6 企業(yè)級應(yīng)用開發(fā)教程
- Visual Basic程序開發(fā)(學(xué)習(xí)筆記)
- React Native Cookbook
- R語言游戲數(shù)據(jù)分析與挖掘
- Hands-On JavaScript High Performance
- Full-Stack Vue.js 2 and Laravel 5
- 零基礎(chǔ)輕松學(xué)SQL Server 2016
- C語言程序設(shè)計教程
- Mastering Data Mining with Python:Find patterns hidden in your data
- Building Wireless Sensor Networks Using Arduino
- Android群英傳
- 快速入門與進(jìn)階:Creo 4·0全實例精講
- Java并發(fā)編程之美