二進制(2):浮點數的一些問題


一些數學符號的解釋:
ϵ\epsilon: 一個無限小的數,微積分中的一個概念。如果a=1-ϵ\epsilon,
a的值無限接近於1。
\sum:求和符號。i=05xi=i5+i4+i3+i2+i1+i0\sum_{i=0}^5x^i=i^5+i^4+i^3+i^2+i^1+i^0
xix_i: x下標,此位(0或1)在位向量中的位置。(最右邊爲第0位)
[00000000]2[0000 0000]_2: 下標2,表示進制。
\infty:無窮。 a=+a=+\infty,a爲無限大的一個數。 a=a=-\infty,a爲無限小的一個數。
CknC_k^n:不考慮順序的組合。從n個元素中取k個元素,k個元素的組合數量爲CknC_k^n


浮點數,例如C語言的float與double,與整數int都是最常用的數據類型,但是與int又有根本上的不同。在應用層面一個是可以表示整數與小數,另一個只能表示整數,另一方面int從二進制變換到十進制是相對直觀的,而float相對複雜,它需要將位向量劃分爲三個部分再通過一個公式換算才能完成換算。本文通過切入五個問題對浮點數及其涉及的機制進行總結。1,什麼是浮點數?小數點是如何浮動的?2,浮點數相對於定點表示法的優勢是什麼?3,目前通用的IEEE754浮點數是如何計算的?4,如果浮點數的賦值不在浮點數可表示的匹配值之內會發生什麼?5,同樣數值的int和float的底層二進制編碼有什麼區別?

1,什麼是浮點數?小數點是如何浮動的?

爲什麼稱爲浮點數?相對的定點數是什麼?

“浮點”一詞相對於“定點”,表示小數點是可移動的。在定點表示法中,我們比較熟悉的是無符號編碼unsigned int和補碼編碼int,在前一篇文章中(二進制(1):無符號編碼和補碼編碼)已對定點表示法進行了一定的總結。回顧一下,在無符號編碼中二進制位向量1111 1111的十進制值爲27+26+25+24+23+22+21+20=2552^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0=255。255也既是255.0,以二進制的角度看,小數點最低有效位的右邊11111111.。定點表示法也可以用來表示小數,例如可以規定讓二進制向量最低四位表示數值的小數部分,那麼位向量1111 1111的值會變爲23+22+21+20+21+22+23+24=15.93752^3+2^2+2^1+2^0+2^{-1}+2^{-2}+2^{-3}+2^{-4}=15.9375,相當於將小數點移到中間1111.1111。
其轉換公式可歸納爲,如果有位向量[bmbm1...b1b0b1b2...bn+1bn][b_m b_{m-1}... b_1 b_0 b_{-1} b_{-2}... b_{-n+1} b_{-n} ],則:
b=i=nm2ibib=\sum_{i=-n}^m2^i*b_i

浮點數的小數點是如何浮動的?

下面將會完全拋棄上文中定點表示法的思路,重新爲位向量0000 0000設計一種二進制轉換十進制的方案,一種簡化版的浮點數。我們設:
V:位向量在新的編碼模式下轉換爲十進制後的值。
E:位向量在老的無符號編碼規則下的值。
V=2EV=2^E
在這種編碼模式下,小數點會發生什麼變化? 假設最底層的位向量爲[0000 0001],那麼V=2[00000001]2=21=2V=2^{[0000 0001]_2}=2^1=2, 最底層的0與1是不會發生變化的,只是因爲制定了新的規則,所以00000001變爲了十進制的2,從另一個角度去分析這種變化,將V變爲unsigned int的二進制位向量,觀察小數點的位置,E–>V == 0000 0001(.)—>0000 0001(0.)。
如果底層位向量爲00000100,V=2[00000100]2=28=256V=2^{[0000 0100]_2}=2^8=256,E–>V == 0000 0100(.)–>0000 0100(000000.)。
因此可以看出,在V=2EV=2^E的這種編碼模式下,隨着底層位向量的值的不斷變化,小數點的位置也是不斷變化的,這種小數點的浮動,也既是“浮點”,其實現的核心既是V=2EV=2^E
再看一下目前各編程語言都在用的IEEE浮點標準:
V=(1)sM2EV=(-1)^s*M*2^E

2,浮點數相對於定點表示法的優勢是什麼?

1,更大的取值範圍
定點表示法的缺點是它能表示的數值範圍相對要小,加入了小數則更加有限,在上文的例子中的8位位向量在有限的位數中既要表示整數又要表示小數,例如上文中的的[1111.1111],它的最大值是23+22+21+20+21+22+23+24=15.93752^3+2^2+2^1+2^0+2^{-1}+2^{-2}+2^{-3}+2^{-4}=15.9375 。但如果用V=2EV=2^E這種浮點取值方式,它的整數部分最大可達2[1111]=215=327682^{[1111]}=2^{15}=32768。十進制21002^{100}在無符號編碼中的二進制表示需要101位才能完成,而在上文的浮點編碼模式下,V2100=2E[1100100]V2^ {100}=2^{E[1100100]},只需要7位。

2,可以更精確的表示小數
小數數量是接近無窮的,想用一堆1和0的組合來精確的表示所有小數是不切實際的,所以任何編碼模式所能表示的小數數量都是有限的,二進制表示小數只是一種模擬,當一個浮點數被賦值爲小數時,它的實際值將會是它能表示的最接近被賦值的那個數的一個匹配值,一個有限長度的位向量能表示的匹配值的數量可以理解爲這個位向量的精度,例如64位double類型能表示的匹配值肯定大於32位的float,所以前者的精度更大。
定點表示法的第二個缺點既是它能表示的匹配值很少,同樣上面定義的1111.1111的小數部分只能表示212223242^{-1},2^{-2},2^{-3},2^{-4}四個值組合出來的值,例如[0000.0001]=0.5, [0000.1001]=0.5+0.0625=0.5625,共有C40+C41+C42+C43+C44=241=15C_4^0+C_4^1+C_4^2+C_4^3+C_4^4=2^4-1=15個數值。而將上文的V=2EV=2^E按照IEEE浮點對小數表示進行擴展V=M2EV=M*2^E,M爲小數部分,暫不考慮負數的情況,同樣一個8位的位向量中,小數位爲4位,整數位爲4位,小數部分可以表示最多(C40+C41+C42+C43+C44)[1111]2=1515=225(C_4^0+C_4^1+C_4^2+C_4^3+C_4^4)*{[1111]_2} =15*15=225個數值。

3,目前通用的IEEE754浮點數是如何計算的?

以上舉例的浮點數規則只是用來理解浮點數的核心概念,目前通用的IEEE754浮點數值的計算公式如下,公式雖短但其實很有深度:
V=(1)sM2E1V=(-1)^s*M*2^E(公式1)
各變量取值範圍:
s:等於1或0,負權,決定該數正負。
M:取值範圍0至2-ϵ\epsilon。有兩個case,規格化時M=1+f,非規格化時M=f,f的取值範圍爲0至1-ϵ\epsilon
E:規格化時爲e-Bias,非規格化時爲1-Bias。e爲底層二進制某部分01通過無符號編碼轉換出來的十進制值,Bias依賴於該部分01位向量長度。

一個32位的IEEE浮點數位向量分爲三個部分:

[0(符號位) 00000000(階碼位) 00000000000000000000000(小數位)]
從右起,0-23位爲小數位,24-31位爲階碼位,32位最高位爲符號位。

符號位:符號位比較好理解,既是公式1中的s,如果符號位爲0,V的值就會爲正,如果符號位爲1,V的值就會爲負。
階碼位:採用無符號編碼規則,階碼位決定了V的取值範圍,它既是公式1中的E=e-Bias中的小e。
小數位:公式1中的M=1+f或M=f的f,計算f的值的方法是也是定點數表示法, 與int不同由於它是表示小數的,所以f=[e23e22e1e0]=e2321+e2222++e1223+e0224f=[e_{23} e_{22}…e_1 e_0]=e_{23}*2^{-1}+e_{22}*2^{-2}+…+e_1*2^{-23}+e_0*2^{-24},也可以理解爲f=224(e23223+e22222++e121+e020)f=2^{-24}*(e_{23}*2^{23}+e_{22}*2^{22}+…+e_1*2^{1}+e_0*2^{0})

三個case:

在不同的情況中,公式1中的階碼E與小數M的計算方式會有差異:

—case1,規格化,階碼位e非全0也非全1。
—case2,非規格化,階碼位e全0。
—case3,特殊情況,階碼位e全1。

1,先分析比較32位的浮點數的case1與case2:

case1規格化:E=eBiasM=1+fBias=211328271=1281=127E=e-Bias,M=1+f,Bias=2^{浮點數的階碼位數-1}-1(32位有8位階碼,既2^7-1=128-1=127)
case2非規格化:E=1BiasM=fE=1-Bias,M=f

case1規格化之中,E的最小值是[00000001]2127=126[0000 0001]_2-127=-126,最大值爲[11111110]212710=254127=127[1111 1110]_2—127_{10} =254-127=127。M的最小值是1+[00...00]2=11+[00...00]_2=1,最大值爲1+[11...11]=(2ϵ)1+[11...11]=(2-\epsilon)非常接近2的一個值。所以V的最大值爲,V=(1)0(2ϵ)2127=(2ϵ)1.70141183510383.41038V=(-1)^0*(2-\epsilon)*2^{127}=(2-\epsilon)*1.701411835*10^{38}\approx3.4*10^{38},此時V的位向量是[0 11111110 11…11]。V的最小值爲與最大值相比只是符號位變爲了1,V=(1)1(2ϵ)21273.41038V=(-1)^1*(2-\epsilon)*2^{127}\approx-3.4*10^{38},V的位向量爲[1 11111110 11…11]。V最接近0的值分別在0的兩端,爲V=(1)0112126=±2126V=(-1)^{0或1}*1*2^{-126}=\pm2^{-126}。注意這個數與下面非規格化的最大與最小值非常接近,這個機制實現了一個規格化到非規格化數的平滑過渡。另一個值得注意的特點是它的數值分部形式是v=x2yv=x*2^{y},如下圖所示越接近0數值分佈越密集,這也導致了浮點數的一個重要屬性既是越接近0的數值精度越高。
在這裏插入圖片描述

case2非規格化之中,E的值是唯一的,1-127=-126。M的最小值是[00...00]=0[00...00]=0,最大值爲[11...11]=(1ϵ)[11...11]=(1-\epsilon)一個非常接近1的值。V的最大值爲,V=(1)0(1ϵ)21262126V=(-1)^0*(1-\epsilon)*2^{-126}\approx2^{-126}。V的最小值與最大值相比只是符號位變爲了1,爲(1)1(1ϵ)21262126(-1)^1*(1-\epsilon)*2^{-126}\approx-2^{-126}。V的最接近0的值爲0,[0 0000000 00…00]。

這裏可以看出,在數軸上從規格化的最小值一直到0的部分,精度達到了峯值,匹配值均勻分佈在數軸上0的兩端,在±2126\pm2^{-126}之間每兩個匹配值的間距爲2242126=21502^{-24}*2^{-126}=2^{-150},不再有“數越小越精確”的性質。

2,再看一下情況3,特殊值:

0 11111111 00…0
當階碼位全爲1時,如果小數位全爲0,此數表示無窮大,\infty,而符號位決定了他是++\infty-\infty。例如當一個float值除以0後或發生溢出情況它的值爲無窮。
0 11111111 00…1
當階碼位全爲1時,如果小數位不全爲0,此數爲NaN,not a number,不是一個數。兩個值爲無窮的float相減結果爲NaN,如果一個float變量被賦值1\sqrt-1,它的值也會變爲NaN。當一個值無法用實數或無窮表達時,它是NaN。

4,如果浮點數的賦值不在浮點數可表示的匹配值之內會發生什麼?

向偶數舍入

與一個整數變量不可能表示所有整數一樣,一個浮點數也無法表示所有小數。因爲浮點數根據長度與格式不同,它只能表示有限的匹配數,如果變量被賦的值不在這些匹配數之中,系統將會去尋找最接近的匹配值,也既是將變量被賦的值進行舍入。IEEE浮點數採用的舍入模式是“向偶數舍入”,這種舍入模式是先尋找最接近的匹配值,如果被舍入的值正好在兩個匹配數的正中間,將再尋找最接近的最低有效位爲偶數的匹配值。這種方案在某些統計場景中是最合理的。
舉例來說,假設某個浮點數可表示的匹配值只有1和2,如果將變量賦值爲1.4,浮點數的值將爲1,1.6爲2,1.5爲2。在二進制的層面理解,可以歸納爲傾向於將最低有效位變爲0,例如有4位浮點數二進制位向量00.00,出現超精度賦值00.0010,00.0010在00.00與00.01之間,這時則選擇舍入爲00.00。

運算中的問題

當兩個浮點數進行運算時,在運算之前他們會分別進行舍入,在運算之後,他們的運算結果將會被進行舍人,由於這種對運算結果的自動舍入,浮點數的運算不具有結合率與分配率
不符合結合率:例如a+b+c不一定等於a+(b+c),在第一次運算後a+b與b+c都有可能得出舍入後的值。
不符合分配率:1e20*(1e20-1e20)\neq(1e20*-1e20)-(1e20*-1e20),前者在第一次運算後會變爲,1e20*0,後者是-\infty*-\infty

int轉換float的問題

當一個int變量強制轉換爲float變量後,它不會溢出,因爲float變量可賦值的範圍要大於int,但是它有可能被舍入成最近的匹配值,因爲int可以精確表示它可被賦值範圍內的所有整數,但是float的可表示數值是以M2EM*2^E的形式分佈在數軸上,數值越大,精確度越低。也既是在整數的絕對值超過一個很大的值的時候,它的匹配值無法全部覆蓋整數。

5,同樣數值的int和float的底層二進制編碼有什麼區別?

例:123451012345_{10}在無符號編碼中的位向量爲
1234510=[11000000111001]212345_{10}=[11000000111001]_2

我們可以嘗試手動將12345的無符號編碼轉換爲IEEE32位浮點數格式編碼:

構造小數位f
1,移小數點至最高有效位的右邊,再將最高有效位轉化爲2的冪,[1.1000000111001]213[1.1000000111001]*2^{13},既是轉化成了V=(1)sM2EV=(-1)^s*M*2^E
2,因爲此數爲規格化數,根據公式1,M=1+frac,可以捨去最高位的1,變爲了[.1000000111001]。
3,低位段添加0使小數位達到23位,[10000001110010000000000]。
構造階碼部分e
4,E=13,bias=127,E=e-bias,e=E+bias=140。14010=[10001100]2140_{10}=[10001100]_2
5,添加符號位,結果爲:
123451012345_{10}=[0 10001100 10000001110010000000000]

可手動轉換說明風馬牛不相及的兩種編碼的機制有某種內在聯繫,轉換之後可以看到一個現象,無符號編碼刪除最高位後的位向量與浮點數小數數位的高位段完全相同。


學習資料:深入理解計算機系統(原書第二版 )—Randal E.Bryant


維護日誌:
2020-1-7:增刪查改,修改部分錯誤

發佈了46 篇原創文章 · 獲贊 55 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章