float類型在內存中的表示

先說一下計算機中二進制的算法:

  • 整數
    整數的二進制算法大家應該很熟悉,就是不斷的除以2取餘數,然後將餘數倒序排列。比如求9的二進制:

    9/2=4 餘 1

    4/2=2 餘 0

    2/2=1 餘 0

    1/2=0 餘 1

    一直計算到商爲0爲止,然後將得到的餘數由下到上排列,就得到了9的二進制:1001。

    從上面的算法我們可以看到,用整數除以2,最終都能夠到0。因此,整數是可以用二進制來精確表示的。
  • 小數
    小數的二進制算法和整數的大致相反,就是不斷的拿小數部分乘以2取積的整數部分,然後正序排列。比如求0.9的二進制:

    0.9*2=1.8 取 1
    0.8*2=1.6 取 1

    0.6*2=1.2 取 1

    0.2*2=0.4 取 0

    0.4*2=0.8 取 0

    0.8*2=1.6 取 1

    … …

    如此循環下去。因此我麼得到的二進制小數也是無限循環的: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的在內存中的存儲結果就出來了:

0  10000001  10111001100110011001100

 

總結一下,實數轉二進制float類型的方法:

A. 分別將實數的整數和小數轉換爲二進制
B. 左移或者右移小數點到第一個有效數字之後
C. 從小數點後第一位開始數出23位填充到尾數部分

D. 把小數點移動的位數,左移爲正,右移爲負,加上偏移量127,將所得的和轉換爲二進制填充到指數部分
E. 根據實數的正負來填充符號位,0爲正,1爲負

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

 

------------------------我是分割線------------------------------

需要注意的東西:

  1. 23位尾數填充的問題
    雖然在IEEE754標準中我沒有找到相應的描述,但是在實際處理的時候,截取23位尾數需要對第24位進行零舍一入的操作,至少在Java虛擬機中是這麼做的。有興趣的可以試試0.7f-0.6f。
  2. 運算時向右對階操作的舍入問題
    這個也是在實際操作時遇到的問題。到目前爲止我還無法確定向右對階操作是否也進行了零舍一入的操作。有興趣的可以試試9.6f-6.9f。
  3. 指數全零問題
    全部爲零的指數說明當前所表示的是一個特殊的float數字。全零的float類型分爲兩種情況:  
    • 尾數全零。此時代表當前float數爲0。根據符號位,分爲+0和-0。這兩個在JVM上相等的。
      這裏需要解釋一下。因爲IEEE的默認1的問題,所以float類型沒有辦法表示0,因此只能在已有的規定上做一些強制性的規則來表示0,也就有了上面的這個全零的說法。
       
    • 尾數不全爲零。此時說明當前的float數是一個非規格化的數。
  4. 指數全一問題
  5. 指數全部爲一也說明這個float數是一個不尋常的數字。它也分爲兩種情況:  
    • 尾數全零。此時根據符號位的不同,分爲正無窮(+infinity)和負無窮(-infinity)。注意,這兩個東西在JVM中是不相等的。
    • 尾數不全爲零。此時表示此float數純粹不是一個數(NaN,Not a Number)。這個NaN也分爲QNaN(Quiet NaN)和SNaN(Signalling NaN)。至於這兩個NaN有什麼區別,下面這段話倒是說明了,但是我沒有這方面的知識,所以不敢妄加翻譯,只好把原文放在這裏:
      A QNaN is a NaN with the most significant fraction bit set. QNaN's propagate freely through most arithmetic operations. These values pop out of an operation when the result is not mathematically defined.
      An SNaN is a NaN with the most significant fraction bit clear. It is used to signal an exception when used in operations. SNaN's can be handy to assign to uninitialized variables to trap premature usage.

      Semantically, QNaN's denote indeterminate operations, while SNaN's denote invalid operations.

      最後一句話說的明白,QNaN就是一個不確定操作的結果,而SNaN純粹就是一個非法的操作結果。

------------------------我是分割線-----------------------------

OK,廢話了這麼多,我覺得對float類型也大致有個瞭解了。float明白了以後,double類型也就好說了,基本和上面一樣,只是指數和尾數的位數不一樣而已。

 

參考:

IEEE Standard 754 Floating Point Numbers: http://steve.hollasch.net/cgindex/coding/ieeefloat.htm

IEEE 754-1985: http://en.wikipedia.org/wiki/IEEE_754-1985

IEEE 754-2008: http://en.wikipedia.org/wiki/IEEE_754-2008

Java 理論與實踐: 您的小數點到哪裏去了?: http://www.ibm.com/developerworks/cn/java/j-jtp0114/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章