組成
IEEE754標(biāo)準(zhǔn)包含一組實(shí)數(shù)的二進(jìn)制表示法菌羽。它有三部分組成:
- 符號位
- 指數(shù)位
- 尾數(shù)位
三種精度的浮點(diǎn)數(shù)各個部分位數(shù)如下:
正規(guī)化
對于將某個實(shí)數(shù)表示為計(jì)算機(jī)浮點(diǎn)數(shù)叔汁,首先要將其正規(guī)化,也就是表示為形如:
的樣子筹我。其中b
是0
或1
,而p
二進(jìn)制數(shù)表示的指數(shù)位。這樣屈糊,假設(shè)想表示為單精度(float)的浮點(diǎn)數(shù),那么:
- 第一位符號位用
0
表示正琼了,用1
表示負(fù) - 將指數(shù)
p
加上移碼表示為8位的二進(jìn)制數(shù) - 在接下來的23位填充位數(shù)
b
部分逻锐。由于正規(guī)化表示時,最左邊部分總是1
雕薪,所以我們只需表示23位的尾數(shù)即可谦去。
移碼
上述中有一個詞:移碼(exponential bias)。因?yàn)橹笖?shù)p
有正有負(fù)蹦哼,那么在8位的指數(shù)位中我們就要拿出第一位來指示符號鳄哭,這樣顯然會造成不必要的浪費(fèi)。給指數(shù)加上移碼纲熏,就能保證結(jié)果總是一個非負(fù)數(shù)妆丘,也就可以將8個指數(shù)位都利用起來锄俄。對于有M
個指數(shù)位的精度,其移碼為:
這樣就得到上面三種精度的移碼:
以雙精度(Double)的為例勺拣。雙精度的指數(shù)位有11位奶赠。這樣可以表示的數(shù)是從
000 0000 0000
到111 1111 1111
,也就是指數(shù)加移碼所表示的范圍從0到2047药有,那么毅戈,減去移碼1023,則可以表示的指數(shù)是-1023到1024愤惰。但是注意苇经,-1023和1024作為他用(后面會說到)。所以實(shí)際上能表示數(shù)的指數(shù)是從-1022到1023宦言。
例子
【例】:求3.14的單精度浮點(diǎn)數(shù)表示扇单。
首先將3.14轉(zhuǎn)成二進(jìn)制:
- 整數(shù)部分3的二進(jìn)制是11
b
- 小數(shù)部分0.14的二進(jìn)制是:0.0010001111010111000010[10001111.....]
b
(方括號中表示小數(shù)點(diǎn)后第23位及之后)。這樣奠旺,3.14的二進(jìn)制代碼就是:11.0010001111010111000010[10001111....]×20
b
蜘澜,那么用正規(guī)化表示就是:1.10010001111010111000010[10001111....]×21b
。
方括號表示的就是小數(shù)點(diǎn)后第24位了响疚,由于單精度浮點(diǎn)數(shù)尾數(shù)只有23位鄙信,所以需要舍入(舍入方法見后):由于第24位為1,且之后 不全為 0忿晕,所以需要向第23位進(jìn)1完成上舍入:1.10010001111010111000011×21b
扮碧。
而其指數(shù)是1,需要加上移碼127杏糙,即128慎王,也就是(1000 0000)b
。
它又是正數(shù)宏侍,所以符號為0赖淤。
綜上所述,3.14的單精度浮點(diǎn)數(shù)表示為:
0 1000-0000 1001-0001-1110-1011-1000-011b
十六進(jìn)制代碼為:0x4048F5C3
通過此例可知谅河,3.14的單精度浮點(diǎn)數(shù)表示是0 1000-0000 1001-0001-1110-1011-1000-011
≡酆担現(xiàn)在我們來還原,看看它的誤差:
- 指數(shù)是128绷耍,那么還原回去(減去移碼)吐限,實(shí)際指數(shù)就是1
- 尾數(shù)還原也就是:10010001111010111000011
b
,所以是:1.10010001111010111000011×21b
褂始,也就是11.0010001111010111000011b
诸典。
利用二進(jìn)制轉(zhuǎn)十進(jìn)制,可得它對應(yīng)的十進(jìn)制數(shù)是:3.1400001049041748046875崎苗。顯然與3.14是有誤差的狐粱。
我們再通過另一種方法估算誤差舀寓。從例子中可知,對于3.14的單精度浮點(diǎn)數(shù)肌蜻,我們舍去了第24位以及之后互墓,它們是:
0.00...(23個0)....00 [10001111.....]×21b
。
為了方便計(jì)算蒋搜,不妨假設(shè)此后全是0(即方括號中省略部分)篡撵,也就是舍去了:
0.10001111b×2-23×21b
約為0.00000013317912817001;由于舍入進(jìn)位關(guān)系豆挽,給第23位又加了1育谬,所以加了:2-23×21,故而要減去這一部分祷杈。
所以,誤差約為2-23×21 - 0.10001111b×2-23×21=0.00000010523945093155渗饮。所以結(jié)果大致為3.14+0.00000010523945093155=3.14000010523945093155
但汞。
可見和上面計(jì)算結(jié)果大致相同。
機(jī)器ε(machine epsilon)
機(jī)器ε表示1與大于1的最小浮點(diǎn)數(shù)之差互站。不同精度定義的機(jī)器ε不同私蕾。以雙精度為例,雙精度表示的1是:
而比1大的最小雙精度浮點(diǎn)數(shù)是:
可見胡桃,此二者之差為:2-52≈2.220446049250313e-16踩叭。所以它就是雙精度浮點(diǎn)數(shù)的機(jī)器ε。
在舍入中翠胰,相對舍入誤差不能大于機(jī)器ε的一半容贝。比如上面的3.14的單精度浮點(diǎn)數(shù),二者誤差絕對值是0.0000001049041748046875....之景,從而相對舍入誤差為0.0000001049041748046875....÷3.14≈
0.00000003340897286773
斤富。而單精度浮點(diǎn)數(shù)的機(jī)器ε為2-23≈1.1920928955078125e-7,它的一半是0.00000005960464477539
锻狗。顯然满力,相對舍入誤差小于單精度浮點(diǎn)數(shù)機(jī)器ε的一半。
非正規(guī)化:0的表示
從正規(guī)化中可知轻纪,無論如何浮點(diǎn)數(shù)都滿足最左邊是1油额。這就有一個嚴(yán)重問題:0沒有辦法被表示。為此刻帚,可以使用非正規(guī)化的表示方法潦嘶,即讓最左邊默認(rèn)為0,這樣再另尾數(shù)也全部為0崇众,就可以表示0了衬以。
新的問題又來了:根據(jù)什么判斷是非正規(guī)化還是正規(guī)化呢缓艳?
答案就是通過指數(shù)部分來反映。記得前面說過看峻,雙精度浮點(diǎn)數(shù)中阶淘,指數(shù)加移碼 的范圍可以從0到2047,然而0和2047是作為他用的互妓。在這里溪窒,指數(shù)部分為0就代表著非正規(guī)化。
所以冯勉,當(dāng)見到指數(shù)部分為0是澈蚌,尾數(shù)部分就不再是1.bbbbb...
而是0.bbbbb...
了。
再進(jìn)一步灼狰,對于非正規(guī)化宛瞄,可以看成是正規(guī)化中,小數(shù)點(diǎn)向左邊跑了一位:1.bbbb....×2-1023=0.1bbbb....×2×2-1023==0.1bbbb....×2-1022(只是概念上理解交胚,小數(shù)第一位也不一定非要是1份汗,如0.001010×2^-1022
也可)。所以蝴簇,非正規(guī)化下表示為:
現(xiàn)在杯活,0就可以表示了。值得注意的是熬词,此時0可以表示位+0和-0旁钧。
因?yàn)樗淖钭筮叢皇?是0,實(shí)際上可以表示更小的數(shù)互拾。雙精度浮點(diǎn)數(shù)下歪今,使用非正規(guī)化可以表示的最小的正數(shù)是0.00......01×2-1022也就是2-52×2-1022=2-1074。
請注意這個最小數(shù)和前面提到的機(jī)器ε的區(qū)別颜矿。比機(jī)器ε小的數(shù)是可以被表示出來的(利用非正規(guī)化)彤委。但是當(dāng)它們與其他浮點(diǎn)數(shù)做運(yùn)算時,因?yàn)橐D(zhuǎn)成同一種格式(正規(guī)化格式)或衡,從而可能會因?yàn)橐绯鑫欢簧釛壗褂啊W罱K結(jié)果就是,這些更小的數(shù)盡管能被表示封断,但是對運(yùn)算結(jié)果沒有影響斯辰。
浮點(diǎn)數(shù)加法
機(jī)器加法要先將兩個操作數(shù)的小數(shù)點(diǎn)對齊,相加后再轉(zhuǎn)為浮點(diǎn)數(shù)存儲坡疼。這里最重要的一點(diǎn)是彬呻,盡管浮點(diǎn)數(shù)有位數(shù)限制,但是加法會在精度更高的寄存器中進(jìn)行,這意味著闸氮,寄存器能夠運(yùn)算出比52位還要多的位數(shù)剪况,但是在轉(zhuǎn)回浮點(diǎn)數(shù)存儲時,多余位數(shù)會被舍棄蒲跨,造成兩者相加的機(jī)器結(jié)果不嚴(yán)格等于算術(shù)結(jié)果译断。
無窮大與NaN
上面說到,在雙精度浮點(diǎn)數(shù)中或悲,指數(shù)為0表示非正規(guī)化孙咪,那么指數(shù)為2047(二進(jìn)制是111 1111 1111b,即11位指數(shù)位全為1)就表示無窮大和NaN(Not a Number)巡语。具體表現(xiàn)在翎蹈,當(dāng)指數(shù)是2047,當(dāng)尾數(shù),全為0就表示無窮大男公,當(dāng)尾數(shù)不全為0就表示NaN荤堪。
舍入規(guī)則
以52位尾數(shù)位的雙精度浮點(diǎn)數(shù)為例,舍入時需要重點(diǎn)參考第53位:
- 若第53位為1枢赔,而第53位之后全部為0澄阳。此時就要使第52位為0:若第52位本來就是0則不管,若第52位為1糠爬,則第53位就要向第52位進(jìn)一位寇荧,這樣第52位就可以為0
- 若不是上面的情況举庶,即第53位1执隧,但是第53位之后不全為0,則第53位就要向第52位進(jìn)一完成上舍入户侥。
- 若也不是上面兩種情況镀琉,那么第53位必為0,此時直接舍去不進(jìn)位蕊唐,稱為下舍入屋摔。
由于存在這種舍入規(guī)則,浮點(diǎn)數(shù)一般在機(jī)器內(nèi)都不會以原數(shù)精確相等的存儲替梨,這就會使在某些情況下钓试,使用浮點(diǎn)數(shù)做算術(shù)運(yùn)算時出現(xiàn)令人費(fèi)解的情況,如在JavaScript中(數(shù)以雙精度存儲):
>>9.4-9-0.4===0 //9.4減去9再減去0.4副瀑,與0比較大小
<<false
>>(9.4-9-0.4).toFixed(20)
<<"0.00000000000000033307"
可見機(jī)器表示中弓熏,9.4-9-0.4不嚴(yán)格等于0,其結(jié)果有極小誤差糠睡。因?yàn)榘凑丈厦娴乃惴芍?.4在機(jī)器內(nèi)被表示為:9.4+0.2×2-49挽鞠,而0.4被表示為0.4+0.1×2-52。這樣,當(dāng)9.4-9時(因?yàn)?是整數(shù)是可以精確存儲的)得0.4+0.2×2-49信认,再減去0.4+0.1×2-52得3×2-53材义,約等于"0.00000000000000033307"。
循環(huán)小數(shù)的二進(jìn)制轉(zhuǎn)回十進(jìn)制的技巧
某循環(huán)小數(shù)的二進(jìn)制碼是:0. 0110 0110 0110 0110 0110.....b嫁赏∑涞啵可見是0110的循環(huán),令x為其十進(jìn)制數(shù):x=0.01100110.....b橄教,則24x=110,01100110.....b清寇,兩式相減得:(24-1)x=110b,即15x=6护蝶,從而x=6/15=0.4