從printf說開去(二)

     (接上回)

        我們在C/C++代碼中使用:

            printf(“%f”, 10/3, 0×40080000 );

        看到運行結果了嗎?爲什麼這行看起來不合乎所謂的語法的printf能輸出3.000000呢?

        翻閱手冊,回顧一下printf的格式化參數說明,你會發現%f的類型是double!
        我們知道在目前32位的機器上,sizeof(double) = 8字節 而sizeof(int) = 4字節,也就是說,機器在處理輸出數據的時候,期望得到一個8字節的數據空間,而實際上只提供了一個4字節的數據空間,是不是這裏出了問題?那麼這個過程到底發生了些什麼呢?

        10/3 這個數據由於是整數類型常數,他並不是在運行時進行計算的,而是在編譯時,編譯器把他翻譯成了3。實質上,這段代碼就與寫printf(“%f”, 3)無異,他們完全等價。

        既然等價,爲什麼使用printf(“%f”, 3.0f);會輸出正確的值?sizeof(3.0f) 不也等於4麼?3.0f 和 整數3 是否等價?3.0和3.0f是否等價?

        似乎有點越扯越遠,大家會不會有點糊塗了?要解釋清楚這個過程確實需要一點口舌,不過別急,我們一個問題一個問題地說吧:


        問題一:printf(“%f”, 3.0f) 與 printf(“%f”, 3)的區別,3.0f和整數3在存儲上有什麼區別?3.0和3.0f在存儲上有什麼區別?

        其實,3.0f和整數3在內存中的表示是完全不同的,他們都佔用4個字節空間,整數3在內存當中就是 0×00000003,而3.0f在內存中卻爲:0×40400000。

        並且,你會發現,3.0和3.0f在內存中的表示也不相同,3.0是一個雙精度浮點數,他在內存中表示爲:0×4008000000000000。你可以通過調試器查看這一點。
        那麼浮點數是按什麼規則表示的呢?IEEE標準從邏輯上用三元組{S,E,M}來表示浮點數N,其中S代表符號位,E代表指數位,M代表尾數。
        如果是單精度浮點數(float):N共32位,其中S佔1位,E佔8位,M佔23位
        如果是雙精度浮點數(double):N共64位,其中S佔1位,E佔11位,M佔52位。

        (注:本文並不打算詳細探討浮點數的表示規則,有興趣的朋友可自行參考IEEE754浮點數標準)
   
            N可以用以下公式算得:
            N = (-1)^S * m * 2^e

            當E的二進制位不全爲0,也不全爲1時,
            e = |E| – bias    (bias = 2^(k-1) – 1)
           單精度時k=8,bias=127 雙精度時k=11,bias=1023

           其中m = |1.M|

           當E的二進制位全部爲0時,此時:
            e = 1- bias
           m = |0.M|

 

           當E的二進制位全爲1時,若M的二進制位全爲0,則n表示無窮大,若S爲1則表示負無窮,S爲0則爲正無窮。若M的二進制位不全爲0時,表示NaN(Not a Number),代表着不合法或未初始化的值。

 

    例如:
    單精度浮點數3.0f,表示爲2進制:
    S |            E         |                              M                                   |
    0  10000000  10000000000000000000000

    N = (-1)^0 * 1.5 * 2^1 = 3.0f

 

    雙精度浮點數3.0,表示爲2進制:
 符號位      指數位                                     尾數位
    S |                E              |                              M                                 |
    0  01000000000  1000 0000 0000 0000 … 0000

 

    看到了嗎?他們的計算原理是一樣的,但是位數不同,導致他們在內存裏面的表示也不相同。


    3    在內存裏面用16進製表示爲:0×00000003
    3.0f 在內存裏面用16進製表示爲:0×40400000
    3.0  在內存裏面用16進製表示爲:0×4008000000000000

    我們現在知道了printf中%f是按double進行處理的,那麼按雙精度浮點規則,我們來看3是怎麼被算成0的?


    3 用 64bit二進制表示:
    0000 0000 0000 0000 0000 0000 0000 … 0000 0011

    S |           E                    |                           M                                   |
    0  00000000000  0000 0000 0000 0000 … 0011


    這裏的m已經是一個非常小,近乎於0的數字了,因此,在float保有的精度範圍內,顯示成爲了0。


    有興趣的同學可以算算,這個值大約等於:1.48乘以負的323次方。

 

    好,現在弄清楚了浮點數的表示,新的問題又來了,printf(“%f”, 10/3, 0×40080000 ); 能顯示3.000000,這又是怎麼工作的呢?

 

(未完待續)

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