官术网_书友最值得收藏!

建議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;
    }
}

主站蜘蛛池模板: 沙坪坝区| 瑞昌市| 漳州市| 安陆市| 诸暨市| 西林县| 页游| 商河县| 梅州市| 和硕县| 通化市| 太康县| 独山县| 建平县| 南乐县| 莱西市| 西充县| 蕉岭县| 云霄县| 抚松县| 灵石县| 乌审旗| 宣化县| 屯留县| 灌南县| 右玉县| 苍溪县| 北票市| 米泉市| 桃江县| 江都市| 长海县| 云南省| 抚松县| 广宁县| 磐石市| 额敏县| 丰县| 滨海县| 额尔古纳市| 平邑县|