printf 輸出浮點數

在測試printf函數輸出結果時,寫了以下代碼:html

/**
 * printf:格式化輸出函數
 *     printf函數不會按照格式控制而對數據類型進行轉換,無論三七二十一,
 *     抓到二進制數據就按照格式控制符對數據進行解析。
 */
#include <stdio.h>
int main(void)
{
    //test_1
    float a = 10.9;
    printf("%d\n", a);
    //以十進制形式輸出帶符號整數(正數不輸出符號)
    //輸出 -1073741824
    //(printf 不會將浮點型變量 a 數據類型轉換爲整型,而是將按照浮點型格式存儲的數據直接按照整型數據的格式進行解析打印)

    //test_2
    int b = 10;
    printf("%f\n", b);
    //以小數形式輸出單、雙精度浮點型
    //輸出 0.000000

    //test_3(數據類型轉換)
    int num_i = 10;
    float num_f = (float)num_i;
    printf("%f\n", num_f);
    //輸出 10.000000
    //直接對變量進行類型強制轉換時:
    //編譯器先將內存中 num_i 的值當作整型數來處理,而後再按照浮點數的格式將其存儲在內存中,最後 printf 以 %f (實數)格式打印出來

    //test_4(與 test_一、test_二、test_3 進行對比)
    int* fa = (int*)&a;
    float* fb = (float*)&b;
    printf("%d\n", *fa); //輸出結果 1093559910
    printf("%d\n", *fb); //輸出結果 0

    getchar();
    return 0;
}

原本覺得test_4的結果會和test_1test_2結果相同,然而結果輸出以下:函數

編譯環境爲 Dev_C++ 5.11,編譯日誌以下:工具

主要觀察a*fa輸出結果:在 test_4 中,取變量 a 的地址,而後將其地址強制轉換成整型數地址,再取其地址中的內容 printf 按照%d格式輸出。在這個過程當中並無改變地址內保存的內容,test_1 和 test_4 都是將其地址中保存的內容按照整型數據進行解析並打印輸出,爲何結果會不同呢??測試

主要參考了下面的幾個帖子:.net

其中的重點是:printf 格式化輸出,在傳遞參數時,若實參爲 float 型數據,編譯時會自動轉換爲 double 類型。我又寫了一些代碼用來測試,並總結這個問題,具體過程以下:3d

1.實型常量默認爲 double 型

#include <stdio.h>
void main()
{
    //實型常量默認爲 double 型,後面加 f/F 認爲是 float 型
    printf("%d\n", sizeof(10.9));  //輸出結果 8
    printf("%d\n", sizeof(10.9f)); //輸出結果 4
}

2.打印以下數據

#include <stdio.h>
int main(void)
{
    float  a = 10.9;
    float  b = 10.9f;
    double c = 10.9;
    double d = 10.9f;
    printf("%d\n", a);             //輸出結果 -1073741824
    printf("%d\n", b);             //輸出結果 -1073741824
    printf("%d\n", c);             //輸出結果 -858993459
    printf("%d\n", d);             //輸出結果 -1073741824
    printf("%d\n", 10.9);          //輸出結果 -858993459
    printf("%d\n", 10.9f);         //輸出結果 -1073741824
    printf("%d\n", (double)10.9f); //輸出結果 -1073741824

    //對比 test_4
    int* fa = (int*)&a;
    printf("%d\n", *fa);           //輸出結果 1093559910

    return 0;
}

3.對比輸出結果--總結

printf("%d\n", a);             //輸出結果 -1073741824
printf("%d\n", b);             //輸出結果 -1073741824
printf("%d\n", d);             //輸出結果 -1073741824
printf("%d\n", 10.9f);         //輸出結果 -1073741824
printf("%d\n", (double)10.9f); //輸出結果 -1073741824

這裏輸出的都是-1073741824,只看前四行,對於 printf 傳遞的實參都是 float 型(因爲 printf 格式化輸出,在傳遞參數時,若實參爲 float 型數據,編譯時都會自動轉換爲 double 類型數據),因此實際輸出結果均等價於第五行,先將 float 型的數據,自動轉換爲 double 類型,再將 8 字節的 double 類型數據按照%d格式解析輸出。調試


printf("%d\n", c);             //輸出結果 -858993459
printf("%d\n", 10.9);          //輸出結果 -858993459

這裏輸出的都是-858993459,對於 printf 傳遞的實參都是 double 型,因此直接將 8 字節的 double 類型數據按照%d格式解析輸出。那麼這裏的輸出結果和上面的輸出結果爲何不一樣??因爲 float 類型和 double 類型直接轉換會涉及到精度問題(看下面的代碼),因此上面 float 類型的 10.9 轉換爲 double 類型的數據和 double 類型的 10.9 在計算機中是不一樣的,因此按照整型數來對數據(01011···)解析,輸出的結果是不一樣的。日誌

#include <stdio.h>
int main()
{
    printf("%.15f\n", 3.14f);
    printf("%.15f\n", (double)3.14f);
    printf("%.15f\n", 3.14);

    return 0;
}

其輸出的結果以下:code


int* fa = (int*)&a;
printf("%d\n", *fa);           //輸出結果 1093559910

這裏輸出的結果是1093559910,和上面的兩種結果都不相同,是由於將 float 類型的變量 a 的地址強制轉換成整型數地址後,*fa將被當作是整型數(地址中保存的內容實際並無改變),因此將*fa傳遞給 printf 函數,這裏printf("%d\n", *fa);只是將變量 a 地址中 4 個字節的數據內容按照%d的格式進行解析並輸出打印出來,所以,輸出結果與上面兩種結果都不一樣。htm

後記:換編譯器

後來我換了VS 2015對程序進行編譯,會直接給出警告,經過看VS 2015編譯器給出的警告或許就能夠直接發現問題了。對於上面 2.打印以下數據 中的程序,VS 2015的編譯信息以下(結合行號看警告信息,注意第18行沒有警告):


原本的程序只是爲了測試 printf 函數的輸出結果,用來驗證「 printf 函數不會按照格式控制符而對數據類型進行強制轉換。對於 printf,無論三七二十一,只是抓到二進制數據就按照格式控制符對數據進行解析。」可是細心觀察,深刻思考,就會發現新大陸:)


補充(兩天後)

能夠打印出變量 a、b、c、d 的地址,而後用調試工具查看數據在計算機內部的存儲,能夠對上述的 總結 加以驗證,請參考 評論 ,具體代碼以下:

#include <stdio.h>

int main(void)
{
    float  a = 10.9;
    float  b = 10.9f;
    double c = 10.9;
    double d = 10.9f;

    //打印變量的地址,查看數據在計算機內部的存儲
    printf("%p\n", &a); // 0x412e6666          float 類型內部存儲
    printf("%p\n", &b); // 0x412e6666          float 類型內部存儲
    printf("%p\n", &c); // 0x4025cccccccccccd  double類型內部存儲
    printf("%p\n", &d); // 0x4025ccccc0000000  由float類型轉換爲double類型,其內部存儲

    printf("\n****************\n");

    //如下的printf輸出語句是等價的
    printf("%d\n", a);                         //輸出結果 -1073741824
    printf("%d\n", b);                         //輸出結果 -1073741824
    printf("%d\n", d);                         //輸出結果 -1073741824
    printf("%d\n", 10.9f);                     //輸出結果 -1073741824
    printf("%d\n", (double)a);                 //輸出結果 -1073741824
    printf("%d\n", (double)b);                 //輸出結果 -1073741824
    printf("%d\n", (double)10.9f);             //輸出結果 -1073741824
    printf("%d\n", 0x4025ccccc0000000);        //輸出結果 -1073741824
    //printf對數據解析:截取低位四個字節 0xc0000000
    //補碼:1100 0000 0000 0000 0000 0000 0000 0000
    //求原碼,符號位不變,其他位取反加 1
    //原碼:1100 0000 0000 0000 0000 0000 0000 0000
    //十進制表示:-1073741824

    printf("\n****************\n");

    //如下的printf輸出語句是等價的
    printf("%d\n", c);                         //輸出結果 -858993459
    printf("%d\n", 10.9);                      //輸出結果 -858993459
    printf("%d\n", 0x4025cccccccccccd);        //輸出結果 -858993459
    //printf對數據解析:截取低位四個字節 0xcccccccd
    //補碼:1100 1100 1100 1100 1100 1100 1100 1101
    //求原碼,符號位不變,其他位取反加 1
    //原碼:1011 0011 0011 0011 0011 0011 0011 0011
    //十進制表示:-858993459

    printf("\n****************\n");

    //對比 test_4
    int* fa = (int*)&a;
    //如下的printf輸出語句是等價的
    printf("%d\n", *fa);                       //輸出結果 1093559910
    printf("%d\n", 0x412e6666);                //輸出結果 1093559910
    //printf對數據解析:0x412e6666
    //補碼:0100 0001 0010 1110 0110 0110 0110 0110
    //求原碼,正數的原碼、反碼和補碼相同
    //原碼:0100 0001 0010 1110 0110 0110 0110 0110
    //十進制表示:1093559910

    getchar();
    return 0;
}

具體輸出結果以下所示:

相關文章
相關標籤/搜索