int轉float時精度損失問題 --暨-- 實現sqrt()函數

1、問題

閒來無事,做了道算法題圖一樂,很簡單,就是實現sqrt()函數的功能,即求解非負整數的平方根,我給出的算法如下:

int mySqrt(int x) {
    if(x == 1)
        return 1;
    float low = 0.0, xtmp =x, high = x;
    float mid = (low + high) / 2.0;
    int re = 0; float comp = 1E-5;
    
    while(high - mid >= 0.0)
    {
        float tmp = mid * mid;
        if(tmp - xtmp > comp)   //tmp大於xtmp
        {
            high = mid;
        }
        else if(xtmp - tmp > comp) //tmp小於xtmp
        {
            low = mid;
        }
        else if( (tmp-xtmp < comp) && (xtmp - tmp < comp) )  //tmp等於xtmp,對於float變量而言,沒有絕對的相等
        {
            re = mid;
            break;
        }
        mid = (low + high) / 2.0;
    }
    return re;
}

我是通過二分查找的方式來求解的,但是當我執行到測試用例:x =  2147395599時,計算出來的結果爲46340.000000,而不是正確答案46339.99998,雖然只差了不到0.1,但是他們計算出來的平方值卻相差比較大的。最後經過調試與分析,發現,在語句float xtmp = x; xtmp的值就已經不是2147395599.000000,而是變成了2147395584.000000,最後猜測可能會與int轉float時的精度損失有關係。

2、問題分析

int轉float時的精度損失是由float型在內存中的存儲方式引起的。關於float類型在內存的存儲可以參考博文:https://blog.csdn.net/yezhubenyue/article/details/7436624,下面就解釋一下該問題出現的原因:

int x = 2147395599,在內存中的存儲爲:1111111111111101010100000001111,有31

float xtmp = x,即將x的值賦值給xtmp,所以按照float類型的存儲方式,需要將小數點左移30位,變成1.111111111111101010100000001111,由於第24爲必須爲1,且不存儲,實際上是.111111111111101010100000001111

這樣xtmp的指數就等於30 ,但存儲時需按照30+127=157的格式來存儲,即:10011101

由於是正數,所以float類型的首位爲0,但float的尾數只有23位(.11111111111110101010000),所以該float型的數據存儲格式爲:0-10011101-11111111111110101010000

那麼該float型二進制串表示的十進制是多少呢,首位爲0表示是正數,指數位爲10011101 = 157,減去127爲30,所以尾數中的小數點需要往右移30位,但實際上只有23位,所以需要補齊,由1.11111111111110101010000 →變爲:111111111111110101010000 0000000.0 = 十進制 2147395584.000000

3、解決

把float類型換位double型就可以了。

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