1. 程式人生 > >從printf說開去(三)

從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,這又是怎麼工作的呢?

(未完待續)