1. 程式人生 > 其它 >IEEE754 浮點數的表示方法

IEEE754 浮點數的表示方法

技術標籤:C

原文地址:https://blog.csdn.net/K346K346/article/details/50487127

1.浮點數的儲存格式

浮點數(Floating-point Number)是對實數的一種近似表示,由一個有效數字(即尾數)加上冪數來表示,通常是乘以某個基數的整數次冪得到。以這種表示法表示的數值,稱為浮點數。表示方法類似於基數為10的科學計數法。利用浮點進行運算,稱為浮點計算,這種運算通常伴隨著因為無法精確表示而進行的近似或舍入。

計算機對浮點數的表示規範遵循電氣和電子工程師協會(IEEE)推出的 IEEE754 標準,浮點數在 C/C++ 中對應 float 和 double 型別,我們有必要知道浮點數在計算機中實際儲存的內容。

IEEE754 標準中規定 float 單精度浮點數在機器中表示用 1 位表示數字的符號,用 8 位表示指數,用 23 位表示尾數,即小數部分。對於 double 雙精度浮點數,用 1 位表示符號,用 11 位表示指數,52 位表示尾數,其中指數域稱為階碼。IEEE754 浮點數的格式如下圖所示。
這裡寫圖片描述
注意,IEE754 規定浮點數階碼 E 採用"指數e的移碼-1"來表示,請記住這一點。為什麼指數移碼要減去 1,這是 IEEE754 對階碼的特殊要求,以滿足特殊情況,比如對正無窮的表示。

2.移碼

移碼(又叫增碼)是對真值補碼的符號位取反,一般用作浮點數的階碼,引入的目的是便於浮點數運算時的對階操作。

對於定點整數,計算機一般採用補碼的來儲存。正整數的符號位為 0,反碼和補碼等同於原碼。負整數符號位為1,原碼、反碼和補碼的表示都不相同,由原碼變成反碼和補碼有如下規則:
(1)原碼符號位為1不變,整數的每一位二進位制數位求反得反碼;
(2)反碼符號位為1不變,反碼數值位最低位加1得補碼。

比如,以一個位元組 8bits 來表示 -3,那麼[ − 3 ] 原 = 10000011 [-3]_原=10000011[−3]原​=10000011,[ − 3 ] 反 = 11111100 [-3]_反=11111100[−3]反​=11111100,[ − 3 ] 補 = 11111101 [-3]_補=11111101[−3]補​=11111101,那麼 -3 的移碼就是[ − 3 ] 移 = 01111101 [-3]_移=01111101[−3]移​=01111101。

如何將移碼轉換為真值 -3 呢?先將移碼轉換為補碼,再求值。

3.浮點數的規格化

若不對浮點數的表示作出明確的規定,同一個浮點數的表示就不是唯一的。例如( 1.75 ) 10 (1.75)_{10}(1.75)10​可以表示成1.11 × 2 0 1.11\times 2^01.11×20,0.111 × 2 1 0.111\times2^10.111×21,0.0111 × 2 2 0.0111\times2^20.0111×22等多種形式。當尾數不為0時,尾數域的最高有效位為1,這稱為浮點數的規格化。否則,以修改階碼同時左右移動小數點位置的辦法,使其成為規格化數的形式。

3.1 單精度浮點數真值

IEEE754 標準中,一個規格化的 32 位浮點數 x 的真值表示為:
x = ( − 1 ) S × ( 1. M ) × 2 e x=(-1)^S\times(1.M)\times2^ex=(−1)S×(1.M)×2e
e = E − 127 e=E-127e=E−127
其中尾數域值是1.M。因為規格化的浮點數的尾數域最左位總是1,故這一位不予儲存,而認為隱藏在小數點的左邊。

在計算指數 e 時,對階碼E的計算採用原碼的計算方式,因此 32 位浮點數的 8bits 的階碼 E 的取值範圍是 0 到 255。其中當E為全 0 或者全 1 時,是 IEEE754 規定的特殊情況,下文會另外說明。

3.2 雙精度浮點數真值

64 位的浮點數中符號為 1 位,階碼域為 11 位,尾數域為 52 位,指數偏移值是 1023。因此規格化的 64 位浮點數 x 的真值是:
x = ( − 1 ) S × ( 1. M ) × 2 e x=(-1)^S\times(1.M)\times2^ex=(−1)S×(1.M)×2e
e = E − 1023 e=E-1023e=E−1023

4.浮點數的具體表示

4.1 十進位制到機器碼

(1)0.5
0.5 = ( 0.1 ) 2 0.5=(0.1)_20.5=(0.1)2​,符號位S為0,指數為e = − 1 e=-1e=−1,規格化後尾數為1.0。

單精度浮點數尾數域共23位,右側以0補全,尾數域:
M = [ 000 0000 0000 0000 0000 0000 ] 2 M=[000\ 0000\ 0000\ 0000\ 0000\ 0000]_2M=[00000000000000000000000]2​

階碼E:
E = [ − 1 ] 移 − 1 = [ 0111 1111 ] 2 − 1 = [ 0111 1110 ] 2 E=[-1]_移-1=[0111\ 1111]_2-1=[0111\ 1110]_2E=[−1]移​−1=[01111111]2​−1=[01111110]2​

對照單精度浮點數的儲存格式,將符號位S,階碼E和尾數域M存放到指定位置,得0.5的機器碼:
0.5 = [ 0011 1111 0000 0000 0000 0000 0000 0000 ] 2 0.5=[0011\ 1111\ 0000\ 0000\ 0000\ 0000\ 0000\ 0000]_20.5=[00111111000000000000000000000000]2​。

十六進位制表示為0.5=0x3f000000。

(2)1.5
1.5 = [ 1.1 ] 2 1.5=[1.1]_21.5=[1.1]2​,符號位為0,指數e = 0 e=0e=0,規格化後尾數為1.1。

尾數域M右側以0補全,得尾數域:
M = [ 100 0000 0000 0000 0000 0000 ] 2 M=[100\ 0000\ 0000\ 0000\ 0000\ 0000]_2M=[10000000000000000000000]2​

階碼E:
E = [ 0 ] 移 − 1 = [ 10000000 ] 2 − 1 = [ 01111111 ] 2 E=[0]_移-1=[1000 0000]_2-1=[0111 1111]_2E=[0]移​−1=[10000000]2​−1=[01111111]2​

得1.5的機器碼:
1.5 = [ 0011 1111 1100 0000 0000 0000 0000 0000 ] 2 1.5=[0011\ 1111\ 1100\ 0000\ 0000\ 0000\ 0000\ 0000]_21.5=[00111111110000000000000000000000]2​

十六進位制表示為1.5=0x3fc00000。

(3)-12.5
− 12.5 = [ − 1100.1 ] 2 -12.5=[-1100.1]_2−12.5=[−1100.1]2​,符號位S為1,指數e為3,規格化後尾數為1.1001,

尾數域M右側以0補全,得尾數域:
M = [ 100 1000 0000 0000 0000 0000 ] 2 M=[100\ 1000\ 0000\ 0000\ 0000\ 0000]_2M=[10010000000000000000000]2​

階碼E:
E = [ 3 ] 移 − 1 = [ 1000 0011 ] 2 − 1 = [ 1000 0010 ] 2 E=[3]_移-1=[1000\ 0011]_2-1=[1000\ 0010]_2E=[3]移​−1=[10000011]2​−1=[10000010]2​

即-12.5的機器碼:
− 12.5 = [ 1100 0001 0100 1000 0000 0000 0000 0000 ] 2 -12.5=[1100\ 0001\ 0100\ 1000\ 0000\ 0000\ 0000\ 0000]_2−12.5=[11000001010010000000000000000000]2​

十六進位制表示為-12.5=0xc1480000。

用如下程式驗證上面的推算,程式碼編譯執行平臺Win32+VC++ 2012:

#include <iostream>
using namespace std;

int main() {
	float a=0.5;
	float b=1.5;
	float c=-12.5;

	unsigned int* pa=NULL;
	pa=(unsigned int*)&a;
	unsigned int* pb=NULL;
	pb=(unsigned int*)&b;
	unsigned int* pc=NULL;
	pc=(unsigned int*)&c;
	
	cout<<hex<<"a=0x"<<*pa<<endl;
	cout<<hex<<"b=0x"<<*pb<<endl;
	cout<<hex<<"c=0x"<<*pc<<endl;
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

輸出結果:

a=0x3f000000
b=0x3fc00000
c=0xc1480000
  • 1
  • 2
  • 3

驗證正確。

4.2 機器碼到十進位制

(1)若浮點數 x 的 IEEE754 標準儲存格式為 0x41360000,那麼其浮點數的十進位制數值的推演過程如下:

0 x 41360000 = [ 0 10000010 011 0110 0000 0000 0000 0000 ] 0x41360000=[0\ 10000010\ 011\ 0110\ 0000\ 0000\ 0000\ 0000]0x41360000=[01000001001101100000000000000000]

根據該浮點數的機器碼得到符號位S=0,指數e=階碼-127=1000 0010-127=130-127=3

注意,根據階碼求指數時,可以像上面直接通過 "階碼-127"求得指數e,也可以將階 碼 + 1 = 移 碼 階碼+1=移碼階碼+1=移碼,再通過移碼求其真值便是指數 e。比如上面階碼10000010 + 1 = 1000001 1 [ 移 碼 ] = > 0000001 1 [ 補 ] = 3 ( 指 數 e ) 10000010+1=10000011_{[移碼]}=>00000011_{[補]}=3(指數e)10000010+1=10000011[移碼]​=>00000011[補]​=3(指數e)。

包括尾數域最左邊的隱藏位1,那麼尾數 1.M=1.011 0110 0000 0000 0000 0000=1.011011。

於是有:
x = ( − 1 ) S × 1. M × 2 e = + ( 1.011011 ) × 2 3 = + 1011.011 = ( 11.375 ) 10 x=(-1)^S\times1.M\times2^e=+(1.011011)\times2^3=+1011.011=(11.375)_{10}x=(−1)S×1.M×2e=+(1.011011)×23=+1011.011=(11.375)10​

通過程式碼同樣可以驗證上面的推算:

#include <iostream>
using namespace std;

int main() {
	unsigned int hex=0x41360000;
	float* fp=(float*)&hex;
	cout<<"x="<<*fp<<endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

輸出結果:

x=11.375
  • 1

驗證正確。

5.浮點數的幾種特殊情況

(1)0 的表示
對於階碼為 0 或 255 的情況,IEEE754 標準有特別的規定:
如果 階碼 E=0 並且尾數 M 是 0,則這個數的真值為 ±0(正負號和數符位有關)。

因此 +0 的機器碼為:0 00000000 000 0000 0000 0000 0000 0000。
-0 的機器碼為:1 00000000 000 0000 0000 0000 0000 0000。

需要注意一點,浮點數不能精確表示 0,而是以很小的數來近似表示 0,因為浮點數的真值等於(以32bits單精度浮點數為例):
x = ( − 1 ) S × ( 1. M ) × 2 e x=(-1)^S\times(1.M)\times2^ex=(−1)S×(1.M)×2e
e = E − 127 e=E-127e=E−127
那麼 +0 的機器碼對應的真值為1.0 × 2 − 127 1.0\times2^{-127}1.0×2−127。同理,-0 機器碼真值為− 1.0 × 2 − 127 -1.0\times2^{-127}−1.0×2−127。

(2)+ ∞ +\infty+∞和− ∞ -\infty−∞的表示
如果階碼 E=255 並且尾數 M 全是0,則這個數的真值為 ±∞(同樣和符號位有關)。因此+ ∞ +\infty+∞的機器碼為:0 11111111 000 0000 0000 0000 0000 0000。− ∞ -\infty−∞的機器嗎為:1 11111111 000 0000 0000 0000 0000 0000。

(3)NaN(Not a Number)
如果 E = 255 並且 M 不是0,則這不是一個數(NaN)。

6.浮點數的精度和數值範圍

6.1 浮點數的數值範圍

根據上面的探討,浮點數可以表示-∞到+∞,這只是一種特殊情況,顯然不是我們想要的數值範圍。

以 32 位單精度浮點數為例,階碼 E 由 8 位表示,取值範圍為 0-255,去除 0 和 255 這兩種特殊情況,那麼指數 e 的取值範圍就是 1-127=-126 到 254-127=127。

(1)最大正數
因此單精度浮點數最大正數值的符號位S=0,階碼E=254,指數e=254-127=127,尾數M=111 1111 1111 1111 1111 1111,其機器碼為:0 11111110 111 1111 1111 1111 1111 1111。

那麼最大正數值:
P o s M a x = ( − 1 ) S × 1. M × 2 e = + ( 1.11111111111111111111111 ) × 2 127 ≈ 3.402823 e + 38 PosMax=(-1)^S\times1.M\times2^e=+(1.111 1111 1111 1111 1111 1111)\times2^{127}\approx3.402823e+38PosMax=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127≈3.402823e+38
這是一個很大的數。

(2)最小正數
最小正數符號位S=0,階碼E=1,指數e=1-127=-126,尾數M=0,其機器碼為0 00000001 000 0000 0000 0000 0000 0000。

那麼最小正數為:
P o s M i n = ( − 1 ) S × 1. M × 2 e = + ( 1.0 ) × 2 − 126 ≈ 1.175494 e − 38 PosMin=(-1)^S\times1.M\times2^e=+(1.0)\times2^{-126} \approx1.175494e-38PosMin=(−1)S×1.M×2e=+(1.0)×2−126≈1.175494e−38

這是一個相當小的數。幾乎可以近似等於0。當階碼E=0,指數為-127時,IEEE754就是這麼規定1.0 × 2 − 127 1.0\times2^{-127}1.0×2−127近似為0的,事實上,它並不等於0。

(3)最大負數
最大負數符號位S=1,階碼E=1,指數e=1-127==-126,尾數M=0,機器碼與最小正數的符號位相反,其他均相同,為:1 00000001 000 0000 0000 0000 0000 0000。

最大負數等於:
N e g M a x = ( − 1 ) S × 1. M × 2 e = − ( 1.0 ) × 2 − 126 ≈ − 1.175494 e − 38 NegMax=(-1)^S\times1.M\times2^e=-(1.0)\times2^{-126} \approx-1.175494e-38NegMax=(−1)S×1.M×2e=−(1.0)×2−126≈−1.175494e−38

(4)最小負數
符號位S=0,階碼E=254,指數e=254-127=127,尾數M=111 1111 1111 1111 1111 1111,其機器碼為:1 11111110 111 1111 1111 1111 1111 1111。

計算得:
N e g M i n = ( − 1 ) S × 1. M × 2 e = + ( 1.11111111111111111111111 ) × 2 127 = − 3.402823 e + 38 NegMin=(-1)^S\times1.M\times2^e=+(1.111 1111 1111 1111 1111 1111)\times2^{127}=-3.402823e+38NegMin=(−1)S×1.M×2e=+(1.11111111111111111111111)×2127=−3.402823e+38

6.2 浮點數的精度

說到浮點數的精度,先給精度下一個定義。浮點數的精度是指浮點數的小數位所能表達的位數。

階碼的二進位制位數決定浮點數的表示範圍,尾數的二進位制位數表示浮點數的精度。以 32 位浮點數為例,尾數域有 23 位。那麼浮點數以二進位制表示的話精度是 23 位,23 位所能表示的最大數是2 23 − 1 = 8388607 2^{23}-1=8388607223−1=8388607,所以十進位制的尾數部分最大數值是 8388607,也就是說尾數數值超過這個值,float 將無法精確表示,所以 float 最多能表示小數點後 7 位,但絕對能保證的為 6 位,即 float 的十進位制的精度為 6~7 位。

64 位雙精度浮點數的尾數域 52 位,因2 52 − 1 = 4 , 503 , 599 , 627 , 370 , 495 2^{52}-1=4,503,599,627,370,495252−1=4,503,599,627,370,495,所以雙精度浮點數的十進位制的精度最高為 16 位,絕對保證的為 15 位,所以 double 的十進位制的精度為 15~16 位。

7.小結

本文操之過急,難免出現編輯錯誤和不當說法,請網友批評指正。不明之處,歡迎留言交流。對浮點數的加減乘除運算還未涉及,後續可能會去學習並記錄學習所得,與大家分享。


參考文獻

[1]百度百科.移碼
[2]百度知道.關於IEEE754標準浮點數階碼的移碼
[3] 白中英.計算機組成原理第四版[M].科學出版社:P16-30
[4]維基百科.浮點數