來源:編程珠璣
前言相比int等整型,float等浮點類型的表示和存儲較為複雜,但它又是一個無法迴避的話題,那麼就有必要對浮點一探究竟了。在計算機中,一般用IEEE浮點近似表示任意一個實數,那麼它實際上又是如何表示的呢?
下面的表達式裡,i的值是多少,為什麼?如果你不確定答案,那麼你應該好好看看本文。
float f = 8.25f;
int i = *(int*)&f;
IEEE浮點標準用
的形式近似表示一個數。並且將浮點數的位表示劃分為三個欄位:
符號(sign)s決定這個數是負數(s=1)還是正數(s=0)。可以用一個單獨的符號s直接編碼符號s。
尾數(signficand)M是一個二進位小數,它的範圍是1~2-ξ或者是0~1-ξ。
n位小數欄位編碼尾數M。
階碼(exponent)E的作用是對浮點數加權,這個權重是2的E次冪(可能是負數)。k位的階碼欄位 編碼階碼E。
在單精度浮點格式(c語言的float)中,s,exp和frac欄位分別為1位,8位和23位,而雙精度浮點格式(c語言中的double)中,s,exp和frac欄位分別為1位,11位和52位。
一個浮點數的常見比特位表示如下:
而根據exp的值,被編碼的值可以分為三大類不同的情況。下面進行一一解釋。
情況1:規格化的值即最普遍的情況,當exp,即階碼域既不為全0,也不為全1的情況。在這種情況下,階碼欄位解釋為以偏置(biased)形式表示有符號整數,即E=exp-Bias,exp是無符號數(1~254)。Bias是一個等於的偏置值,對於單精度來說,k=23,Bias=127,因此E的範圍是-126~+127。
frac被描述為小數值,且0≤frac<1,其二進位表示為0.frac。尾數定義為 M=1+frac ,則M=1.frac。那麼就有1≤M<2,由於總是能夠調整階碼E,使得M在範圍1≤M<2,所以不需要顯示的表示它,這樣還能獲得一個額外的精度位。也就是說,在計算機內部保存M時,默認這個數的第一位總是1,因此可以被捨去,只保存後面的frac部分,等到讀取的時候,再把第一位的1加上去。
情況2:非規格化的值當exp,即階碼域為全0時,所表示的數便為非規格化的值,該情況下的階碼值E=1-Bias(注:為從非格式化值轉換到格式化值提供了一種方法)。尾數M=frac
非規格化的數有兩個作用。
情況3:特殊值有兩種
浮點數的範圍和有效位對於浮點數,其能表示的數值範圍和其有效位如下
類型比特位數值範圍有效位float32-3.410^38~+3.410^386~7位double64-1.710^-308~1.710^30815~16位long double128-1.210^-4932~1.210^493218~19位可見同比特位數的整型(例如int)要比浮點數(例如float)能表示的數值範圍要小很多,但是需要注意的,雖然浮點數能表示的範圍大,但是 它卻不能精確表示在其範圍內的所有實數,也就是說,它只能保證有效位的值是精確的,當表示的數值(小數部分)超過有效位時,所表示的數是無法保證精確的,甚至可以說是錯誤的。
那麼浮點數的數值範圍和有效位是如何得到的呢?
有了前面了基礎,我們就可以來計算浮點數的數值範圍了。以單精度(float)為例,我們知道它的指數範圍(即E)為-126~+127,而M的範圍為1≤M<2,實際上,對於單精度,1≤M≤2-2^(-23)(注:23為frac欄位所佔的比特位)。那麼我們就可以得到單精度的最大值為:
同理,我們可以得到單精度的最小值為:
我們僅僅以單精度為例,用同樣的方法可以計算其他精度的浮點數數值範圍,在此不再贅述。
浮點數的有效位有效位也可以理解為我們常說的精度。浮點數的精度是由尾數的位數來決定的。
對於單精度(float),它的尾數為23位,而2^23=8388608,共7位,也就是說最多能有7位有效數字,但至少能保證6位,因此其有效位為6~7位。當然我們可以通過下面的內容進一步理解。以下計算結果保留10位小數。
觀察a和b的結果可以發現,0.0000001和0.0000002之間的其他數是沒有辦法通過單精度浮點數來精確表示的,也就是說,只有到小數點後面7位的值才是精確的,同理,觀察b和c的結果,0.0000002到0.0000004之間的其他數也是不能通過單精度浮點數精確表示的,更不幸地是,這之間的數,甚至只能精確到第6位。
這也就有了單精度浮點數的有效位為6~7位的結論。根據相似的方法,我們同樣可以得到雙精度浮點數的有效位為15~16位的結論,這裡不再贅述。
浮點數在內存中的存儲了解了這麼多,我們來看一下一個小數究竟是如何在內存中存儲的。以float f = 8.5f為例。其二進位表示為,可見指數實際值為3,則根據E=exp-Bias,可知exp=E+Bias=3+127=130,根據M=1+frac,可知,frac=M-1=0.0001(二進位)而
因此不難得到,8.5的在內存中的存儲情況為:
如果這個時候把這個值作為整型使用,是多少呢?沒錯,是1091043328
#include<stdio.h>
int main(int argc,char *argv[])
{
float f=8.5f;
int *i = (int*)&f;
printf("%d
",*i);
return 0;
}
關於浮點數,需要再說幾句:
返回 上一級 搜索「Java 女程式設計師 大數據 留言送書 運維 算法 Chrome 黑客 Python JavaScript 人工智慧 女朋友 MySQL 書籍 等關鍵詞獲取相關文章推薦。