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

建議3-2:避免使用浮點數進行精確計算

前面已經闡述過,由于計算機的字長有限,浮點數能夠精確表示的數是有限的。因此在進行數值計算時,有可能要對計算得到的中間結果數據使用相關的舍入規則來取近似值,而這就會導致計算過程產生誤差。示例如代碼清單1-18所示。

代碼清單1-18 浮點數計算示例


#include <stdio.h>
#include <limits.h>
float average(float *arr,size_t size);
enum { array_size = 10 };
int main(void)
{
    float arr[array_size];
    size_t i=0;
    for (i = 0; i < array_size; i++)
    {
            arr[i] = 5.1;
    }
    printf("total / size = %f\n", average(arr,array_size));
    return 0;
}
float average(float *arr,size_t size)
{
    float total = 0.0;
    size_t i=0;
    if (size > 0&&size<=SIZE_MAX)
    {
            for (i = 0; i < size; i++)
            {
                total += arr[i];
                printf("arr[%d] = %f , total = %f\n",
                        i, arr[i], total);
            }
            return total / size;
    }
    else
    {
            return 0.0;
    }
}

在代碼清單1-18中,程序取了10個相同的浮點數(5.1)來計算其平均值。理論上,由于這10個浮點數是相同的,都是5.1,因此所計算得出的平均值也應該是5.1。但實際計算結果并非如此,如圖1-28所示。

圖1-28 代碼清單1-18在GCC中的運行結果

是什么原因導致產生圖1-28所示的結果呢?

如果你詳細看完建議3-1中的內容,相信找出答案并不難。其實,之所以得到這樣的運行結果,歸根結底就是因為5.1無法精確地表達為相應的浮點數,而只能保存為經過舍入計算的近似值。這個近似值再進行累加運算之后,自然無法產生精確的結果,最后的平均值結果也就自然而然地產生了誤差。

為了讓大家能夠更加清楚地看見其結果的舍入情況,筆者把代碼清單1-18里的浮點數保留到小數點22位,如下所示:


printf("total / size = %.22f\n", average(arr,array_size));…
printf("arr[%d] = %.22f , total = %.22f\n", i, arr[i], total);

運行調整后的代碼清單1-18,你就可以清楚地看見其舍入結果,如圖1-29所示。

圖1-29 調整后的代碼清單1-18在GCC中的運行結果(浮點數保留22位小數)

當然,這種結果并不是唯一的,如果編譯器不同,舍入的值也有可能會不同。例如,在VC++2010中運行調整后的代碼清單1-18的結果如圖1-30所示。

圖1-30 調整后的代碼清單1-18在VC++2010中的運行結果(浮點數保留22位小數)

由此可以看出,浮點數據會因為其舍入誤差而導致運算結果產生誤差,從而失去精確性。因此,我們應該盡量避免使用浮點數進行精確計算。

當然,對于代碼清單1-18,我們還可以通過整數代替浮點數的方法來執行內部加法,以保證其結果的精確性。而浮點數只用在打印結果及執行除法計算算術平均值的時候,如代碼清單1-19所示。

代碼清單1-19 整數代替浮點數示例


#include <stdio.h>
#include <limits.h>
float average(int *arr,size_t size);
enum { array_size = 10 };
int main(void)
{
    int arr[array_size];
    size_t i=0;
    for (i = 0; i < array_size; i++)
    {
            arr[i] = 510;
    }
    printf("total / size = %f\n", average(arr,array_size));
    return 0;
}
float average(int *arr,size_t size)
{
    int total = 0;
    size_t i=0;
    if (size > 0&&size<=SIZE_MAX)
    {
            for (i = 0; i < size; i++)
            {
                total += arr[i];
                printf("arr[%d] = %f , total = %f\n", i,
                       (float)arr[i]/100, (float)total/100);
            }
            return (float)(total/ size)/100;
    }
    else
    {
            return 0.0;
    }
}

上面代碼的運行結果如圖1-31所示。雖然采用這種方法就可以避免浮點數的舍入誤差帶來的不精確性,但在一般情況下不建議這樣做。

圖1-31 代碼清單1-19在GCC中的運行結果

主站蜘蛛池模板: 永定县| 阿拉尔市| 日土县| 武穴市| 庐江县| 紫阳县| 丹阳市| 晋州市| 蓬安县| 阿瓦提县| 灌云县| 翁源县| 鄂托克前旗| 天柱县| 宕昌县| 宁河县| 瑞昌市| 鹿泉市| 白银市| 柯坪县| 林口县| 滨州市| 平顶山市| 屯昌县| 专栏| 五台县| 阿尔山市| 交口县| 乐亭县| 呼伦贝尔市| 钟祥市| 方山县| 绥滨县| 上犹县| 凉城县| 平昌县| 敦化市| 大厂| 仪陇县| 西贡区| 伊川县|