1. 程式人生 > >浮點數與位元組資料轉換詳解

浮點數與位元組資料轉換詳解

一、浮點數在記憶體中的表示

對於浮點型別的資料採用單精度型別(float)和雙精度型別(double)來儲存,float資料佔用32bit,double資料佔用64bit。不論是float還是double在儲存方式上都是遵從IEEE的規範的,float遵從的是IEEE R32.24 ,而double 遵從的是R64.53。
無論是單精度還是雙精度在儲存中都分為三個部分:
    1. 符號位(Sign)            :      0代表正,1代表為負
    2. 指數位(Exponent):      用於儲存科學計數法中的指數資料,並且採用移位儲存
    3. 尾數部分(Mantissa):  尾數部分
其中float的儲存方式如下圖所示:
這裡寫圖片描述


而雙精度的儲存方式為:
這裡寫圖片描述

R32.24和R64.53的儲存方式都是用科學計數法來儲存資料的。因此本文僅僅針對單精度浮點數進行分析,雙精度同理可得。

二、單精度浮點數的儲存實現

單精度浮點數儲存表示

首先來看單精度浮點型float。float佔用4位元組空間,也就是32位。從左向右數,第1位是符號位(0代表正數,1代表負數),接著是8位指數位,剩下的23位是資料位(實際上有效數字位是24位,因為第一位有效數字總是“1”,不必儲存)。如下所示

31 30    23 22                    0
S  EEEEEEEE DDDDDDDDDDDDDDDDDDDDDDD

在這儲存實數的四個位元組中,將最高地址位元組的最高位編號為31,最低地址位元組的最低位編號為0,則實數各個部分在這32個二進位制位中的分佈是這樣的:31位是實數符號位,30位是指數符號位,29—23是指數位,22—0位是有效數字位(注意第一位有效數字是不出現在記憶體中的,它總是“1” )。

單精度浮點數轉換步驟

將一個float型轉化為記憶體儲存格式的步驟為:
(1)先將這個實數的絕對值化為二進位制格式。
(2)將這個二進位制格式實數的小數點左移或右移n位,直到小數點移動到第一個有效數字的右邊。
(3)從小數點右邊第一位開始數出二十三位數字放入第22到第0位。
(4)如果實數是正的,則在第31位放入“0”,否則放入“1”。
(5)如果n是左移得到的,說明指數是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”。
(6)如果n是左移得到的,則將n減去1後化為二進位制,並在左邊加“0”補足七位,放入第29到第23位。如果n是右移得到的或n=0,則將n化為二進位制後在左邊加“0”補足七位,再各位求反,再放入第29到第23位。

以12.5為例進行說明:
(1)12.5實數絕對值二進位制形式是 1100.1。
(2)向左移動3位,轉換為科學計數法是1.1001E3,此時n=3。
(3)將小數點右邊第一位開始輸出23位放入第22到第0位,即資料位D為:

    DDDDDDDDDDDDDDDDDDDDDDD
    10010000000000000000000

(4)12.5為整數,因此第31位放入“0”,即S=0。
(5)n是左移得到的,指數為正,則第30位放入“1”。
(6) n減去1為2,轉換為二進位制,左邊加“0”補足七位,放入第29到第23位。
此時,由(5)(6)得指數位E為:

    EEEEEEEE
    10000010

綜上,得到12.5的二進位制儲存為:

31 30    23   22                    0
S  EEEEEEEE   DDDDDDDDDDDDDDDDDDDDDDD
0  10000010   10010000000000000000000

驗證

這裡寫圖片描述

補充:

因為浮點數1.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格式表示是:

31 30    23   22                    0
S  EEEEEEEE   DDDDDDDDDDDDDDDDDDDDDDD
0  01111111   00000000000000000000000

其中最左邊一位是第31位,最右邊一位是第0位。

三、儲存位元組資料轉換為單精度浮點數

轉換為單精度浮點數步驟

將一個記憶體儲存的float二進位制格式轉化為十進位制的步驟:
(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”加上正號或負號即可。

同樣以12.5的二進位制為例:
(1) 在最左邊補一位“1”,得到二十四位有效數字。將小數點點在最左邊那個“1”右邊。

    1.10010000000000000000000

(2) 取出第29到第23位所表示的值n。由於30位是“1”,所有將n增1為0000011(即n=3)
(3) 由於30位是“1”,將小數點右移3位,得到二進位制實數為:

    1100. 10000000000000000000

(4) 轉換為十進位制數,由於31位為“0”,所有結果為12.5

四、程式碼實現

#include <stdio.h>


/*
*function:ftoc(float fvalue,unsigned char*arr)
*decription:  浮點數轉化成四個位元組
*input: 浮點數 
*output: 4個位元組的位元組陣列
*/
//例如12.5--0x41 48 00 00;轉換完之後,arr[0]-00,arr[1]-00,arr[2]-48,arr[3]-41
void ftoc(float fvalue,unsigned char*arr) 
{
    unsigned char  *pf;
    unsigned char *px;
    unsigned char i;   //計數器 
    pf =(unsigned char *)&fvalue;            /*unsigned char型指標取得浮點數的首地址*/  
    px = arr;                               /*字元陣列arr準備儲存浮點數的四個位元組,px指標指向位元組陣列arr*/

    for(i=0;i<4;i++)
    {
        *(px+i)=*(pf+i);     /*使用unsigned char型指標從低地址一個位元組一個位元組取出*/
    }
}

/* 
*function:float ByteToFloat(unsigned char* byteArray) 
*decription:  將位元組型轉化成32bits浮點型 
*input:       長度為4的位元組陣列 
*output: 
*/  
float ByteToFloat(unsigned char* byteArray)  
{  
    return *((float*)byteArray);  
}  

int main(int argc, char *argv[])
{

    int i;
    unsigned char byteArray[4];
    ftoc(12.5,byteArray);

    for(i=0;i<4;i++)
        printf("%x  ",byteArray[i]);

    float x=0;
    x = ByteToFloat(byteArray);
    printf("\n%f  ",x);


    return 0;
}