【轉】float類型在內存中的表示


http://www.cnblogs.com/onedime/archive/2012/11/19/2778130.html

http://blog.csdn.net/adream307/article/details/7246993

http://wenku.baidu.com/link?url=Q_SYeQffEjdS1cpMXIRncmmhwKA_o2978-0ei1_gz9ym2vrmmBrSEZArpE6tR4yCB9PEHLG_FHRakijbr9-Y0DIK_MTjBUTKoXUhgIYETB3

 

ZC:

    float f;
    int j = 0x7FFFFFFF;
    memcpy(&f, &i, sizeof(i));

 

一、

浮點型變量在計算機內存中占用4字節(Byte),即32-bit。遵循IEEE-754格式標准。
一個浮點數由2部分組成:底數m 和 指數e。
                          ±mantissa × 2exponent
(注意,公式中的mantissa 和 exponent使用二進制表示)
底數部分 使用2進制數來表示此浮點數的實際值。
指數部分 占用8-bit的二進制數,可表示數值范圍為0-255。 但是指數應可正可負,所以IEEE規定,此處算出的次方須減去127才是真正的指數。所以float的指數可從 -126到128.
底數部分實際是占用24-bit的一個值,由於其最高位是e位 ,所以最高位省去不存儲,在存儲中只有23-bit。
到目前為止, 底數部分 23位 加上指數部分 8位 使用了31位。那么前面說過,float是占用4個字節即32-bit,那么還有一位是干嘛用的呢?   還有一位,其實就是4字節中的最高位,用來指示浮點數的正負,當最高位是1時,為負數,最高位是0時,為正數。
   浮點數據就是按下表的格式存儲在4個字節中:
                    Address+0       Address+1              Address+2              Address+3
Contents     SEEE EEEE     EMMM MMMM     MMMM MMMM     MMMM MMMM      S: 表示浮點數正負,1為負數,0為正數
      E: 指數加上127后的值的二進制數
      M: 24-bit的底數(只存儲23-bit)
主意:這里有個特例,浮點數 為0時,指數和底數都為0,但此前的公式不成立。因為2的0次方為1,所以,0是個特例。當然,這個特例也不用人為去解決,編譯器會自動去識別。


      通過上面的格式,我們下面舉例看下4.5在計算機中存儲的具體數據:
                    Address+0                 Address+1               Address+2            Address+3
Contents        0x40                         0x90                           0x00                      0x00     接下來我們驗證下上面的數據表示的到底是不是4.5,從而也看下它的轉換過程。
由於浮點數不是以直接格式存儲,他有幾部分組成,所以要轉換浮點數,首先要把各部分的值分離出來。
                  Address+0      Address+1                  Address+2             Address+3
格式         SEEEEEEE     EMMMMMMM       MMMMMMMM     MMMMMMMM
二進制     01000000         10010000               00000000                00000000
16進制     40                         90                            00                            00
       可見:
       S: 為0,是個正數。
       E:為 10000001   轉為10進制為129,129-127=2,即實際指數部分為2。
       M:為 00100000000000000000000。 這里,在底數左邊省略存儲了一個1,使用 實際底數表示為 1.00100000000000000000000
       到此,我們吧三個部分的值都拎出來了,現在,我們通過指數部分E的值來調整底數部分M的值。調整方法為:如果指數E為負數,底數的小數點向左移,如果指數E為正數,底數的小數點向右移。小數點移動的位數由指數E的絕對值決定。
      這里,E為正2,使用向右移2為即得:
      100.100000000000000000000
至次,這個結果就是4.5的二進制浮點數,將他換算成10進制數就看到4.5了,如何轉換,看下面:
小數點左邊的100 表示為 (1 × 22) + (0 × 21) + (0 × 20), 其結果為 4。
小數點右邊的 .100… 表示為 (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + ... ,其結果為.5 。
以上二值的和為4.5, 由於S 為0,使用為正數,即4.5 。
所以,16進制 0x40900000 是浮點數 4.5 。

上面是如何將計算機存儲中的二進制數如何轉換成實際浮點數,下面看下如何將一浮點數裝換成計算機存儲格式中的二進制數。
舉例將17.625換算成 float型。
首先,將17.625換算成二進制位:10001.101   ( 0.625 = 0.5+0.125, 0.5即 1/2, 0.125即 1/8 如果不會將小數部分轉換成二進制,請參考其他書籍。) 再將 10001.101 向右移,直到小數點前只剩一位 成了 1.0001101 x 2的4次方(因為右移了4位)。此時 我們的底數M和指數E就出來了:
底數部分M,因為小數點前必為1,所以IEEE規定只記錄小數點后的就好,所以此處底數為   0001101 。
指數部分E,實際為4,但須加上127,固為131,即二進制數 10000011 
符號部分S,由於是正數,所以S為0.
綜上所述,17.625的 float 存儲格式就是:
0 10000011 00011010000000000000000
轉換成16進制:0x41 8D 00 00
所以,一看,還是占用了4個字節。

 

二、

float一共32位,其結構定義如下:

|-------- 31 -------|------------ 30-23 ------------ |------------ 22-0 ------------|

   符號位(sign)         指數部分(exp)                       小數部分(mag)

sign:符號位就一位,0表示正數,1表示負數

exp: 指數部分,無符號正數

mag:小數部分,定點小數,小數點在最左邊。

float的表達式 :  pow(-1,sign)  *  (1+mag)  * pow(2,exp-127)

 

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc,char *argv[])
{
    float f;
    int i;
    int sign;
    int exp;
    int mag;
    float d_mag;
    float f2;

    sscanf(argv[1],"%f",&f);
    //f=-0.12;
    i = *(int*)&f;
    sign = (i>>31)&0x01;
    exp = (i>>23)&0xFF;
    mag = i&0x7FFFFF;
    d_mag = 1.0f*mag/0x800000;
    f2 = (sign==0?1:-1)*(1+d_mag)*pow(2,exp-127);
    printf("float:f=%f\n",f);
    printf("sign=%X,exp=%X,mag=%X\n",sign,exp,mag);
    printf("float:f2=%f\n",f2);
    return 0;
}

 

參考文獻:http://en.wikipedia.org/wiki/IEEE_754-1985

 

三、

 

進制的算法:

 

 

整數

 

 

整數的二進制算法大家應該很熟悉,就是不斷的除以

2

取余數,然后將余數倒序排

列。

 

 

 

小數

 

 

小數的二進制算法和整數的大致相反,就是不斷的拿小數部分乘以

2

取積的整數部

分,然后正序排列。比如求

0.9

的二進制:

 

 

0.9*2=1.8 

 

 

0.8*2=1.6 

 

 

0.6*2=1.2 

 

 

0.2*2=0.4 

 

 

0.4*2=0.8 

 

 

0.8*2=1.6 

 

 

… … 

 

如此循環下去。因此我么得到的二進制小數也是無限循環的:

0.11100110011... 

 

從小數的二進制算法中我們可以知道,如果想讓這種算法停止,只有在小數部分是

0.5

的時候才可以,但是很不幸,這類的小數很少。所以大部分小數是很難用二進制

來精確表示的。

 

 

 

------------------------

我是分割線

------------------------------

 

 

OK

,有了上面的知識,我們進入正題:看看

float

類型在內存中是如何表示的。

 

 

 

 

float

類型又稱為單精度浮點類型,在

 

 

 

IEEE 754-2008

 

 

 

中是這樣定義它的結構的:

 

 

 

  S 

 

    EEEEEEEE 

 

     FFFFFFFFFFFFFFFFFFFFFFF 

 

31 

  30 

       23 

   22 

                              0

 

 

 

float

類型總共

4

個字節

——

32

位:

 

 

1.

符號位

 

其中最左邊的為符號位,

0

為正,

1

為負。

 

 

2.

指數

 

接下來的

E

是指數,一共

8

位,也用二進制來表示。

 

 

3.

尾數

 

最后的

F

是小數部分,

尾數正是由這

23

位的小數部分

+1

位組成的。

這個稍后解釋)

 

 

 

這里我們需要多說一下指數。雖然指數也是用

8

位二進制來表示的,但是

IEEE

在定義它的時候

做了些手腳,使用了偏移來計算指數。

 

 

 

 

IEEE

 

規定,在

 

float

 

類型中,用來計算指數的偏移量為

127

 

。也就是說,如果你的指數實際是

0

 

 

那么在內存中存的就是

 

0+127=127

 

的二進制。稍后我們來看這個到底如何使用。

 

 

 

 

 

好了,看了這么多,我們該演示一下計算機如何將一個十進制的實數轉換為二進制的。就拿

 

6.9

 

這個數字來舉例吧。

 

-_-||!

 

 

 

 

 

首先,我們按照上面說的方法,分別將整數和小數轉換成對應的二進制。這樣

6.9

 

的二進制表示

 

就是

 

110.1110011001100...

。這里就看出來了,

 

6.9

 

轉換成二進制,小數部分是無限循環的,這在

 

現在的計算機系統上是無法精確表示的。這是計算機在計算浮點數的時候常常不精確的原因之

 

一。

 

 

 

 

其次,將小數點左移(或右移)到第一個有效數字之后。說的通俗些,就是把小數點移到第一個

 

1

之后。這樣的話,對於上面

 

 

110.1110011001100...

 

我們就需要把小數點左

 

2

 

位,得到

 

1.101110011001100...

 

 

 

 

 

 

接下來的事情就有意思了。

首先我們把得到的

 

1.101110011001100..

這個數,

 

從小數點后第一位開

 

始,數出

 

23

 

個來,填充到上面

 

float

內存結構的尾數部分(就是那一堆

 

F

 

的地方),我們這里數

 

出來的就是

 

10111001100110011001100

。這里又要發生一次不精確了,小數點后超出

 

 

 

23

 

位的部

分都將被舍棄,太慘了。

 

 

 

 

 

不過,

 

這里有一個可能讓大家覺得特別坑爹的事情,

 

就是小數點前面的

 

1

 

也不要了。仔細看看上

面的內存結構,確實沒有地方存放這個

 

1

 

。原因是這樣的:

 

IEEE

 

覺得,既然我們大家都約定把

 

小數點移動到第一個有效數字之后,那也就默認小數點前面一定有且只有一個

1

 

,所以把這個

 

1

 

存起來也浪費,干脆就不要了,以后大家都這么默契的來就好。這也是為什么我上面說尾數是

 

23

 

 

+1

位的原因。

 

 

 

 

 

填充完尾數,

 

該填充指數了。

這個指數就是剛才我們把小數點移動的位數,

 

左移為正,

 

右移為負,

 

再按照上面所說的偏移量算法,我們填充的指數應該是

 

2+127=129

 

。轉換成

8

 

位二進制就是

10000001

 

 

 

 

 

最后,根據這個數的正負來填充符號位。我們這里是正數,所以填

 

0

 

。這樣

 

6.9

 

的在內存中的存

 

儲結果就出來了:

 

 

 

 

 

 10000001 

 10111001100110011001100

 

 

總結一下,實數轉二進制

float

類型的方法:

 

 

A. 

分別將實數的整數和小數轉換為二進制

 

B. 

左移或者右移小數點到第一個有效數字之后

 

C. 

從小數點后第一位開始數出

23

位填充到尾數部分

 

 

D. 

把小數點移動的位數,左移為正,右移為負,加上偏移量

127

,將所得的和轉換為二進制填

充到指數部分

 

E. 

根據實數的正負來填充符號位,

0

為正,

1

為負

 

如果需要把

float

的二進制轉換回十進制的實數,只要將上面的步驟倒着來一邊就行了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM