關於浮點數

一、浮點的精度限制

    看了看《深入理解計算機系統》中有關信息存儲的內容,不得不感嘆浮點數的存儲真讓人望而生畏。下面就看看編程中遇到浮點數需要注意的東西吧。

    浮點數的存儲格式與整數完全不同。大部分的實現採用的是IEEE 754標準,float類型,是1個sign bit,8 exponent bits,23 mantissa bits。而double類型,是1個sign bit,11 exponent bits,52 mantissa bits。至於浮點如何去表示小數,請自行查找《深入理解計算機系統》中的相關章節。由於float使用的小數表示方法,導致浮點數值是有精度限制的。

    有限的精度就引發了浮點數值使用時的兩個陷阱:

    1、交換定律不適用浮點數

    如有三個浮點數float x=1/3,y=1/6,z=1/7,而x*y/z不等於x*(y/z)。而對於整數來說,如果不發生溢出的情況下,x*y/z是等於x*(y/z)。

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 1/3;
    float y = 1/6;
    float z = 1/7;

    if (x*y/z != x*(y/z)) {
        printf("Not equal!\n");
    }

    return 0;
}
    2、浮點數的比較要使用範圍比較

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 0.123-0.11-0.013;

    if (x == 0) {
        printf("x is 0!\n");
    }

    if (-0.0000000001 < x && x < 0.0000000001) {
        printf("x is in 0 range!\n");
    }

    return 0;
}
    這兩個都是比較常見的浮點陷阱,下面要說明的是浮點數值的兩個exception。
    (1)infinite無限
    (2)NaN即Not a Number
    其中NaN爲最爲特殊的一個“浮點值”——它不是一個合法的浮點值。請看下面的例子:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x = 1/0.0;

    printf("x is %f\n", x);//infinite

    x = 0/0.0;

    printf("x is %f\n", x);//NaN

    return 0;
}
    當1除以0.0時,我們得到的是infinite,而是用0除以0.0時,得到的就是NaN。這裏完全是普通的除法運算,也會產生NaN的情況。那麼當使用除法的時候,對除數進行檢查,保證其不爲0.0是否就可以避免NaN了呢?再看下面的代碼:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    float x;

    while (1) {
        scanf("%f", &x);
        printf("x is %f\n", x);
    }

    return 0;
}
    示例代碼中,調用scanf來得到用戶輸入的浮點數。令人驚訝的是,scanf作爲C庫函數是接受浮點數的這兩種exceptions的,用戶可以直接輸入無限inf和NaN。而C庫中究竟有多少種輸入輸出函數支持這兩種exception不爲所知。那麼對於UI程序來說,當遇到浮點數值的時候,我們必須要判斷該浮點數是否爲一個合法的浮點數,要對用戶輸入值進行檢查,或者說對於一切不屬於本模塊的浮點輸入值都要進行檢查。以上面的代碼爲例,應該爲:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    float x;

    while (1) {
        scanf("%f", &x);

        if (isinf(x)) {
            printf("It's infinite\n");
        }
        if (isnan(x)) {
            printf("It's NaN\n");
        }

        printf("x is %f, 0x%X\n", x, *(int*)&x);
    }

    return 0;
}
    其中isinf和isnan爲C庫提供的檢測函數,分別用於檢查infinite和NaN。而isnan實際上就是返回x != x,利用的就是NaN的特性,與任何數值進行相等比較都是返回false。所以當x != x時,即爲NaN浮點值。

    好吧。就到這了,以後注意下就行咯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章