浮點數是小數點位置變化的數,能表示的范圍比定點數大很多。
比如二進制數11.11可以表示為111.1×2-1或1.111×21等,我們由此規律能得到二進制數更一般形式N=2E×F,E稱為階碼,F稱為尾數。這個數在機器里怎么存呢,是把正負符號、2、E、E的正負號、F轉為01序列存起來嗎?
如果由我們自己來設計計算機,這樣的確可以,不過總有人會想出更聰明的辦法。首先符號位必須占一位,2可以省略不存,默認的。階碼E轉為移碼存,這樣就不用存階碼的正負符號了。最后是很講究的尾數,尾數為0當然直接存0,不為0時,尾數域的最高位必須為1,比如0.001×20必須要變0.1×2-2,也就是小數點后不能為0,像0.001小數點后為0,就要變化階碼,使得小數點移到1前變成0.1,然后只存小數點之后的數,這個過程叫尾數規格化。
所以一般來說,浮點數的機器碼表示如下:
符號位 | 階碼 (移碼) | 尾數(定點小數) |
---|
現實中具體到底如何實現有許多方法,比如階碼放在尾數后,符號位放在階碼后,階碼用補碼表示等。
用一道題目來幫助理解:
如圖1所示為計算機中16位浮點數的表示格式。
某機器碼為1110001010000000。
若階碼為移碼且尾數為反碼,其十進制真值為 (1) ;
若階碼為移碼且尾數為原碼,其十進制真值為 (2) ;
若階碼為補碼且尾數為反碼,其十進制真值為 (3) ;
若階碼為補碼且尾數為原碼,其十進制真值為 (4) ,將其規格化后的機器碼為 (5) 。
(1) ~ (4)
A.0.078125
B.20
C.1.25
D.20.969375
(5)
A.1110001010000000
B.11110101000000
C.1101010100000000
D.11110001010000
我的分析:
(1)階碼為移碼,即1110為移碼,對應原碼為0110,其十進制真值為6,尾數001010000000為反碼,正數反碼換成原碼為原形式,其對應十進制真值為2-2+2-4=0.3125,所以整個數十進制真值為26×0.3125=20
(2)階碼為移碼,尾數為原碼,和(1)是一樣的。
(3)階碼為補碼,補碼1110對應的原碼為1010,即-2,尾數為反碼,(1)已經分析過為0.3125,則整個數十進制真值為2-2×0.3125=0.078125
(4)階碼1110為補碼,真值為-2,尾數為原碼,同(3)
(5)浮點數規格化表示,是尾數值不為0時,尾數域最高有效位要為1,比如0.001×20必須要變0.1×2-2, 題目中尾數域為001010000000第一個0為符號位不管,第二個0是可以消去的,變成010100000000,這樣消去后,階碼要相應地減1變為1101,故規格化后的機器碼1101010100000000
有個名叫IEEE-754的標准就統一了浮點數具體實現方法,目前大多數高級語言都按照IEEE-754標准來規定浮點數的存儲格式,比如Java,還有我們熟知的C語言。
按IEEE-754標准定義的單精度浮點數float:
符號位 | 階碼 (移碼) | 尾數(定點小數) |
---|---|---|
0位 | 1~8位 | 9~31位 |
雙精度浮點數double
符號位 | 階碼 (移碼) | 尾數(定點小數) |
---|---|---|
0位 | 1~11位 | 12~63位 |
IEEE-754中尾數的規格化有所不同,之前我們說尾數規格化規定小數點后為1,IEEE-754中就將此固定的1設為默認值去掉了,因此尾數域表示的值為1.xxxx(只存儲xxxx,1是默認有的),這樣使得尾數表示范圍多上一位。
IEEE-754中階碼的移碼也有特別規定,要除去階碼的全0和全1狀態,因此像float中階碼的取值不是0~255,而是1 ~ 254,也就是說這里的階碼的移碼不是簡單地由補碼變符號而來,還要減1,換個說法就是偏移量不為128了,而為127。所以對於float來說,真正指數的范圍為-126 ~+127,所表示的數范圍也就為2-126 ~2127即10-38 ~1038
舉個栗子
將176.0625表示為符合IEEE-754標准的單精度浮點數。
- 首先將176.0625化為二進制:10110000.0001
- 尾數規格化:1.01100000001×27,因此尾數域求出來了,為01100000001
- 階碼的移碼:7的二進制為00000111,移碼為10000110。
- 拼接:0 10000110 01100000001000000000000
尾數域記得要補0使尾數有23位。