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型就可以了。