IEEE745浮點數格式

IEEE745浮點數格式
2008-06-05 14:37
昨天在分析一個二進制文件時發現裏面的浮點數既不是BIG_ENDIAN也不是LITTLE_ENDIAN,順序有點怪。迫不得已今天查了查IEEE745標準的浮點數格式,論述的最明白的在
http://www.moon-soft.com/program/bbs/readelite10020.htm
轉貼如下
【 在 ilovezks (ilovezks) 的大作中提到: 】
: 我想求教一個問題:
:   c語言中FLOAT 是如何表示的?尾數,階碼是如何在32位上安排的,即哪幾位是
: 尾數,哪幾位是階碼,那一位是符號位。聽說與CPU有關,是真的嗎?

     在C++裏,實數(float)是用四個字節即三十二位二進制位來存儲的。其中有1位符號位,8位指數位和23位有效數字位。實際上有效數字位是24位,因爲第一位有效數字總是“1”,不必存儲。
     有效數字位是一個二進制純小數。8位指數位中第一位是符號位,這符號位和一般的符號位不同,它用“1”代表正,用”0“代表負。整個實數的符號位用“1”代表負,“0”代表正。
     在這存儲實數的四個字節中,將最高地址字節的最高位編號爲31,最低地址字節的最低位編號爲0,則實數各個部分在這32個二進制位中的分佈是這樣的:31 位是實數符號位,30位是指數符號位,29---23是指數位,22---0位是有效數字位。注意第一位有效數字是不出現在內存中的,它總是“1”。
    
     將一個實數轉化爲C++實數存儲格式的步驟爲:
     (1)先將這個實數的絕對值化爲二進制格式,注意實數的整數部分和小數部分化爲二進制的方法是不同的。
     (2)將這個二進制格式實數的小數點左移或右移n位,直到小數點移動到第一個有效數字的右邊。
     (3)從小數點右邊第一位開始數出二十三位數字放入第22到第0位。
     (4)如果實數是正的,則在第31位放入“0”,否則放入“1”。
     (5)如果n 是左移得到的,說明指數是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”。
     (6)如果n是左移得到的,則將n減去一然後化爲二進制,並在左邊加“0”補足七位,放入第29到第23位。如果n是右移得到的或n=0,則將n化爲二進制後在左邊加“0”補足七位,再各位求反,再放入第29到第23位。
    
     將一個計算機裏存儲的實數格式轉化爲通常的十進制的格式的方法如下:
     (1)將第22位到第0位的二進制數寫出來,在最左邊補一位“1”,得到二十四位有效數字。將小數點點在最左邊那個“1”的右邊。
     (2)取出第29到第23位所表示的值n。當30位是“0”時將n各位求反。當30位是“1”時將n增1。
     (3)將小數點左移n位(當30位是“0”時)或右移n位(當30位是“1”時),得到一個二進制表示的實數。
     (4)將這個二進制實數化爲十進制,並根據第31位是“0”還是“1”加上正號或負號即可。

     特別地,實數0用C++的float格式表示是00000000000000000000000000000000。
    
     如果還不太明白,這裏舉幾個例子。
     一。將23.56化爲C++的float格式。
     (1)將23.56化爲二進制後大約是“10111.1000111101011100001”。
     (2)將小數點左移四位,得到“1.01111000111101011100001”。
     (3)這已經有了二十四位有效數字,將最左邊一位“1”去掉,得到“01111000111101011100001”。將它放入第22到第0位。
     (4)因爲23.56是正數,因此在第31位放入“1”。
     (5)由於我們把小數點左移,因此在第30位放入“1”。
     (6)因爲我們是把小數點左移4位,因此將4減去1得3,化爲二進制,並補足七位得到0000011,放入第29到第23位。
     完畢。
     如果把最左邊定爲第31位,最右邊定爲第0位,那麼在C++裏,float格式的23.56是這樣表示的:01000001101111000111101011100001。相應地-23.56就是這樣表示的:11000001101111000111101011100001。

     二。將實數0.2356化爲C++的float格式。
     (1)將0.2356化爲二進制後大約是0.00111100010100000100100000。
     (2)將小數點右移三位得到1.11100010100000100100000。
     (3)從小數點右邊數出二十三位有效數字,即11100010100000100100000放入第22到第0位。
     (4)由於0.2356是正的,所以在第31位放入“0”。
     (5)由於我們把小數點右移了,所以在第30位放入“0”。
     (6)因爲小數點被右移了3位,所以將3化爲二進制,在左邊補“0”補足七位,得到0000011,各位取反,得到1111100,放入第29到第23位。
     完畢。因此0.2356用C++的float格式表示是:00111110011100010100000100100000。其中最左邊一位是第31位,最右邊一位是第0位。

     三。將實數1.0化爲C++的float格式。
     (1)將1.0化爲二進制後是1.00000000000000000000000。
     (2)這時不用移動小數點了,這就是我們在轉化方法裏說的n=0的情況。
     (3)將小數點右邊的二十三位有效數字00000000000000000000000放入第22到第0位。
     (4)因爲1.0是正的,所以在第31位裏放入“0”。
     (5)因爲n=0,所以在第30位裏放入“0”。
     (6)因爲n=0,所以將0補足七位得到0000000,各位求反得到1111111,放入第29到第23位。
     完畢。所以實數1.0用C++的float格式表示是:00111111100000000000000000000000。其中最左邊一位是第31位,最右邊一位是第0位。
這是IEEE短實數格式,適合X86cpu。


轉換的C程序參考
http://www.x5dj.com/blog/00148315/00132755.shtml
要實現轉換,實際上就是使用不同類型的指針來讀取內存中的特定單元長度的數據
這裏使用了 指針類型的強制轉換:(float *)sp_ch和:(char*)sp_f

下面提供了一個標準C的函數,實現互換:

#include "stdio.h"
void main(void)
{
float a=2.584;
/*2.584的IEEE754格式爲0x40256042*/
char fchar[4];
char *ptr_c;
float *ptr_f;

fchar[3]=0x40;
fchar[2]=0x25;
fchar[1]=0x60;
fchar[0]=0x42;
 
/*高位內存的數據,因爲存放時候要注意順序*/

ptr_c=(char *)&a;
printf("Addr of Float:%p/nAddr of Float Plus 1:%p/n",&a,&a+1);
printf("%f:0x%x%x%x%x/n",a,*(ptr_c+3),*(ptr_c+2),*(ptr_c+1),*ptr_c);
/*高位在高位,低位在低位*/

printf("%f:%p/n",*((float *)(fchar)),(float *)(fchar));
}

單獨的函數實現

float IEEE754_Conv(unsigned char in_char[])
/*2.584:0x40256042*/
{
unsigned char tmp_char[4];
uchar msg[20];
tmp_char[0]=in_char[3];
tmp_char[1]=in_char[2];
tmp_char[2]=in_char[1];
tmp_char[3]=in_char[0];
sprintf(msg,"%.3f",*((float *)tmp_char));
return ((float)atof(msg));
}

其它:浮點數有兩個例外。數0.0存儲爲全零。無限大數的階碼存儲爲全1,尾數部分全零。符號位指示正無窮或者負無窮。

理論參考:基於IEEE 754的浮點數存儲格式分析研究 http://www.studa.net/yingyong/080502/15414593.html

雙精度格式
    IEEE雙精度浮點格式共64位,佔2個連續32位字,包含三個構成字段:52位的小數f,11位的偏置指數e,1位的符號位s。將這2個連續的32位字整體作爲一個64位的字,進行重新編號。其中0:51位包含52位的小數f;52:62位包含11位的偏置指數e;而最高位,第63位包含符號位s。
f[31:0]存放小數f的低32位,其中第0位存放整個小數f的最低有效位LSB,第31位存放小數f的低32位的最高有效位MSB。
    在另外的32位的字裏,第0 到19位,即f[51:32],存放小數f的最高的20位,其中第0位存放這20位最高有效數中的最低有效位LSB,第19位存放整個小數f的最高有效位 MSB。第20到30位,即e[52:62],存放11位的偏置指數e,其中第20位存放偏置指數的最低有效位LSB,第30位存放最高有效位MSB。最高位,第31位存放符號位s。
    在Intel x86結構計算機中,數據存放採用小端法(little endian),故較低地址的32位的字中存放小數f的f[31:0]位。而在在SPARC結構計算機中,因其數據存放採用大端法(big endian),故較高地址的32位字中存放小數f的f[31:0]位。

(1)擴展雙精度格式(SPARC 結構計算機)
    該4倍精度浮點環境符合IEEE關於擴展雙精度格式的定義。該浮點環境的4倍精度浮點格式共128位,佔4個連續32位字,包含3個構成字段:
112位的小數f,15位的偏置指數e,和1位的符號s。將這4個連續的32位字整體作爲一個128位的字,進行重新編號。其中0:110位包含小數f;112: 126位包含偏置指數e;第127位包含符號位s。如圖3所示。
在SPARC結構計算機中,地址最高的32位字存放小數的32位最低有效位,即f[31:0];但是在PowerPC結構計算機中,卻是地址最低的32位字存放這些位。
緊鄰的兩個32位字(在SPARC機中向下計算,在PowerPC機中向上計算)分別存放f[63:32]和f[95:64]。
    最後一個字的第0到15位存放小數的最高16位,即f[111:96]。其中第0位存放該16位的最低有效位,第15位存放整個小數f的最高有效位。第 16到30位存放15位的偏置指數e,其中第16位存放偏置指數的最低有效位,第30位存放它的最高有效位。最高位,第31位存放符號s。

(2)擴展雙精度格式(Intel x86結構計算機)
    該浮點環境雙精度擴展格式符合IEEE雙精度擴展格式的定義。該浮點環境的擴展雙精度格式共80位,佔3個連續32位字,包含四個構成字段:63位的小數 f,1位顯式前導有效位(explicit leading significand bit)j,15位偏置指數e,和1位符號位s。將這3個連續的32位字整體作爲一個96位的字,進行重新編號。其中0:63包含63位的小數f,第63 位包含前導有效位j,64:78位包含15位的偏置指數e,最高位第79位包含符號位s。
    在Intel結構系計算機中,這些字段依次存放在十個連續的字節中。但是,由於 UNIX System V Application Binary Interface Intel 386 Processor Supplement (Intel ABI) 要求雙精度擴展參數,從而佔用堆棧中3個相連地址的32位字,其中最高一個字的高16位未被使用。
    地址最低的32位字存放小數f的低32位,即f[31:0]。其中第0位存放整個小數f的最低有效位LSB 第31位存放小數低32位的最高有效位MSB。
    地址居中的32位字,第0到30位存放小數f的31位最高位,即f[62:32]。其中第0位存放31位最高小數位的最低有效位LSB,第30位存放整個小數的最高有效位,地址居中的32位字的最高位第31位存放顯式的前導有效位j。
    地址最高32位字裏,第0到14位存放15位的偏置指數e,第0位存放偏置指數的最低有效位LSB,第14位存放最高有效位MSB,第15位存放符號位 s。雖然地址最高的32位字的高16位在Intel x86結構系列機種未被使用,但他們對符合Intel ABI的規定來說,是必需的。
發佈了4 篇原創文章 · 獲贊 1 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章