對於浮點類型數據,首先我們需要明白的一點是:浮點數和整型數的編碼方式是很不一樣的,IEEE浮點標准采用V = (-1)s×M×2E的形式來表示一個數,其中符號s決定是負數(s=1)還是正數(s=0),由1位符號位表示。有效數M是一個二進制小數,它的范圍在1~2-ε之間(當指數域E既不全為0也不全為1,即浮點數為規格化值時。ε為有效數M的精度誤差,比如當有效數為23位時,ε為2-24),或者在0~1-ε之間(當指數域全為0,即浮點數為非規格化值時),由23位或52位的小數域表示。指數E是2的冪,可正可負,它的作用是對浮點數加權,由8位或者11位的指數域表示。
下面我們來詳細地剖析IEEE浮點數據表示,相信在認真研讀完以下內容之後,你對浮點數的存儲將會有非常清晰的認識。不用擔心文中會充滿數學家才會考慮的算式和公式,雖然大多數人認為IEEE浮點格式晦澀難懂,但理解了其小而一致的定義原則之後,相信你能感覺到它的優雅和溫順,以下的敘述將盡量用淺顯易懂的語言和例子讓大家心情舒暢地了解浮點存儲的內幕。
--------------------------------------分割線 --------------------------------------
編輯推薦:
C語言中簡單的for循環和浮點型變量 http://www.linuxidc.com/Linux/2013-08/88514.htm
C語言陷阱:浮點運算 http://www.linuxidc.com/Linux/2013-05/83695.htm
C語言中浮點數精度進行截斷輸出 http://www.linuxidc.com/Linux/2010-06/26612.htm
如何在C++語言中對浮點數進行格式化處理 http://www.linuxidc.com/Linux/2009-03/19166.htm
--------------------------------------分割線 --------------------------------------
以32位浮點數為例,其存儲器的內部情況是這樣的:
符號域指數域小數域
S(1位)
E(8位)
M(23位)
從上述公式V = (-1)s×M×2E可以計算出一個浮點數具體的數值,要理解這三個數據域是如何被解釋的,我們需要知道:浮點數的編碼根據指數域的不同取值表示被分成三種情形:
1、規格化值。
當指數域不全為0且不全為1的時候即為這種情形,這是最常見的情況。這時有效數域被解釋為小數值f,且 0≤f<1,其二進制表示為0.fn-1fn-2…f1f0,而有效數M被解釋為為M=1+f。同時指數域被解釋為偏置形式的有符號數,就指數E被定義為E=e-Bias,其中e就是指數域的二進制表示,Bias是一個等於2k-1-1的偏置值(k為指數域位數,對於32位浮點數而言是8,對於64位浮點數而言是11)。我們可以調整指數域E來使得有效數M的范圍在1≤M<2之間,也就可以始終使得M的第一位是1,從而也沒有必要在有效數域中顯式地表示它了。
我們來做一個透視浮點數存儲的練習,以此來更好地理解以上內容。例如浮點數12345.0,其二進制表示為(1.1000000111001)2×213,顯然符號位S應該為0,而指數域部分E應該根據公式E= e-Bias計算,e就是我們想要的內部存儲表示,Bias在32位單精度浮點中的值為127,因此e=E+Bias=13+127=140,用二進制表示即為(10001100)2,而小數域就是在其二進制表示的基礎上減去最高位的1即可,即0.1000000111001,補滿23位小數位,即可得到0.10000001110010000000000,存儲時略去前面的的整數部分和小數點,因此浮點數12345.0的IEEE浮點表示為:
0
1000 1100
100 0000 1110 0100 0000 0000
2、非規格化值。
當指數域全為0時屬於這種情形。非規格化編碼用於表示非常接近0.0的數值以及0本身(因為規格化編碼時有效數M始終大於等於1),我們會看到,由於在非規格化編碼時指數域E被解釋為一個定值:E = 1-Bias = -126(而不是規格化時E = e-Bias),使得規格化數值和非規格化數值之間實現了平滑過渡。另外,此時有效數M解釋為M=f,對比上面所講的規格化編碼,此時的有效數M就是小數域的值,不包含開頭的1。
舉個例子,編碼為如下情形的浮點數就是一個非規格化的樣本:
0
0000 0000
000 0000 0000 0000 0000 0001
這個數值的大小,就應該被解釋為(-1)0×(0.00000000000000000000001)2×21-Bias,即
2-23×2-126 = 2-149,轉成十進制表示大約等於1.4×10-45,實際上這就是單精度浮點數所能表達的最小正數了。
以此類推,規格化值和非規格化值所能表達的非負數值范圍如下所示:
指數域
小數域
32位單精度浮點數
值
十進制
0
0000 0000
000 0000 0000 0000 0000 0000
0
0.0
最小非規格化數
最大非規格化數
最小規格化數
最大規格化數
0000 0000
0000 0000
0000 0001
1111 1110
000 0000 0000 0000 0000 0001
111 1111 1111 1111 1111 1111
000 0000 0000 0000 0000 0000
111 1111 1111 1111 1111 1111
2-23×2-126
(1-ε)×2-126
1×2-126
(2-ε)×2127
≈1.4×10-45
≈1.2×10-38
≈1.2×10-38
≈3.4×1038
1
0111 1111
000 0000 0000 0000 0000 0000
1×20
1.0
規格化和非規格化值非負數值范圍
從上表中可以看出,由於在非規格化中將指數域數值E定義為E = 1-Bias,實現了其最大值與規格化的最小值平滑的過渡(最大的非規格化數為(1-ε)×2-126,只比最小的規格化數2-126小一點點,ε為2-24)。
2、特殊數值。
當指數域全為1時屬於這種情形。此時,如果小數域全為0且符號域S=0,則表示正無窮+∞,如果小數域全為0且符號域S=1,則表示負無窮-∞。如果小數域不全為0時,浮點數將被解釋為NaN,即不是一個數(Not a Number)。比如計算負數平方根或者處理未初始化數據時。
以下是理清各種數據之間關系的總結:
1. 浮點數值V = (-1)s×M×2E。
2. 在32位和64位浮點數中,符號域s均為1位,小數域位數n分別為23位和52位,指數域位數k分別為8位和11位。
3. 對於規格化編碼,有效數M = 1+f,指數E = e-Bias(e即為k位的指數域二進制數據,對於32位浮點數而言e的范圍是1~254,此時E的范圍是-126~127)。
4. 對於非規格化編碼,有效數M = f,指數E = 1-Bias(這是一個常量)。
5. Bias為偏置值,Bias = 2k-1-1,k即為指數域位數,在32位和64位浮點數中k分別為8和11。
6. f為小數域的二進制表示值,即n位的小數域fn-1fn-2…f1f0將被解釋為f=(0.fn-1fn-2…f1f0)2。
有了以上的背景知識之後,我們就可以更從容地分析浮點運算了。畢竟我們不是數學家,學習浮點數不是為了科學研究,更多地是從實用主義的角度出發,是為了要寫出更好更可靠的代碼。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-05/101243p2.htm