C語言中printf用%d輸出float型別資料,或以%f輸出int型資料的結果
1.測試程式及結果
- 程式
#include"stdio.h" int main() { float a = 7.5, b = 1.23, c = 1.24, d = 1.25; double a1 = 7.5, b1 = 1.23, c1 = 1.24, d1 = 1.25; int e = 0, f = 1,g= 0x7fffffff; unsigned int h = 0xffffffff; unsigned long long i = 0x401E000000000000,j= 0x3FF3AE147AE147AE,k= 0x3FF3D70A3D70A3D7,l= 0x3FF4000000000000; printf("7.5_ d:%d\n7.5_x:0x%x\n7.5_llx:0x%llx\n", a,a,a); printf("7.5_ d:%d\n7.5_x:0x%x\n7.5_llx:0x%llx\n\n",a1,a1 ,a1); printf("1.23_ d:%d\n1.23_x:0x%x\n1.23_llx:0x%llx\n", b,b,b); printf("1.23_ d:%d\n1.23_x:0x%x\n1.23_llx:0x%llx\n\n", b1,b1,b1); printf("1.24_ d:%d\n1.24_x:0x%x\n1.24_llx:0x%llx\n", c,c,c); printf("1.24_ d:%d\n1.24_x:0x%x\n1.24_llx:0x%llx\n\n",c1,c1, c1); printf("1.25_ d:%d\n1.25_x:0x%x\n1.25_llx:0x%llx\n",d,d, d); printf("1.25_ d:%d\n,1.25_x:0x%x\n1.25_llx:0x%llx\n\n",d1,d1, d1); printf("0_f:%f\n\n",e); printf("1_f:%f\n\n", f); printf("0x7fffffff_f:%f\n\n",g); printf("0xffffffff_f:%f\n\n", h); printf("0x401E000000000000_f:%f\n\n", i); printf("0x3FF3AE147AE147AE_f:%f\n\n",j); printf("0x3FF3D70A3D70A3D7_f:%f\n\n", k); printf("0x3FF4000000000000_f:%f\n\n",l); return 0; }
- 結果:
圖1
2.結果分析
- IEEE754標準
圖2
名稱 長度 位元位置
符號位 Sign (S) : 1bit (b31)
指數部分Exponent (E) : 8bit (b30-b23)
尾數部分Mantissa (M) : 23bit (b22-b0)
其中的指數部分(E)採用的偏置碼(biased)的形式來表示正負指數,若E<127則為負的指數,否則為非負的指數。
另外尾數部分M儲存的是當把一個浮點數規範化表示後的1.zozooz...(二進位制的)形式的zozooz的部分的位元串,共23位.
求值方法: (-1)^S*(1.M)*2^(E-127) (公式1)
注意:%f輸出float型別,輸出6位小數,有效位數一般為7位;
(2)雙精度(64位)浮點數的結構:圖3
名稱 長度 位元位置
符號位 Sign (S) : 1bit (b63)
指數部分Exponent (E) : 11bit (b62-b52)
尾數部分Mantissa (M) : 52bit (b51-b0)
雙精度的指數部分(E)採用的偏置碼為1023
求值方法:(-1)^S*(1.M)*2^(E-1023) (公式2)
注意:雙精度數也可用%f格式輸出,它的有效位一般為16位,給出小數6位。(這一點在計算金額的時候尤為重要,超過有效位的數字是無意義的,一般會出錯。)
- 具體分析過程
1. float a=7.5, doule a1=7.5 結果解釋:
(7.5)10=(111.1)2=1.111*2^2;
以Float在記憶體中儲存:
S=0;
E=(2+127)10=10000001;
M=111;
圖4
以Double在記憶體中儲存:
S=0;
E=(2+1023)10=( 10000000001)2
M=111;
圖5
雖然7.5在記憶體中以float(32Bit)儲存,但是在printf函式輸出的時候要轉換為double(64Bit)位的結構(原因:因為float和double型別的資料用printf函式輸出的時候都是以%f,沒有區別,所以編譯器在輸出的時候,無論是double還是float型別都已double儲存形式輸出),所以在程式執行結果中無論是以float型別還是double型別儲存的7.5用%llx輸出出來都是0x401E000000000000,但是用%d和%x輸出的都是0,這是因為%d和%x只取記憶體的低32位,從圖5可以看出低32全為0,所以取出來為0.
2.float b=1.23,double b1= 1.23 結果解釋:
(1.23)10=( 1.0011101011100001010001111010111)2= 1.0011101011100001010001111010111*2^0
以float型別在記憶體中儲存:
S=0;
E=(0+127)10=01111111
M=0011101 0111000010100100
圖6
以double型別在記憶體中儲存:
S=0;
E=(0+1023)10=(01111111111)2
M=0011 10101110 00010100 01111010 11100001 01000111 10101110
圖7
可以發現圖1中將1.23定義為float型別和double型別打印出來的結果不一樣,為什麼會有這一現象的發生呢?有兩個原因,第一:千萬不要以為凡是計算機輸出的數字都是精確的。必須知道有些浮點數是沒法用二進位制精確表示的,例如這裡的1.23,在記憶體中是近似儲存的;第二:回到前面解釋過的一個問題,float型別在記憶體中是以(32BIt)儲存,首先將1.23以float的形式儲存在記憶體中,存進去的是一個近似的數字,如圖8所示,近似為1.230000019073486328125,16進製表示為:0x3f9d70a4,當用printf函式列印的時候,將1.230000019073486328125(而不是1.23)轉換為double型別,然後輸出,所以結果為圖9所示;
圖8
3. 1.24類似1.23 1.25類似7.5 這裡不再解釋
4. 整形數字以 %f 輸出 的結果說明unsigned int h = 0xffffffff,執行結果解釋;
這個可以將上面的思路反過來理解,%f每次列印需要獲取記憶體的64bit 並且用IEEE754 雙精度浮點數的編碼規則解碼, unsigned int h = 0xffffffff 在記憶體儲存形式如圖10,可以看出他的指數位為0,用公式2計算這是一個非常小的數。所以顯示出來是0;
圖10 unsigned long long i = 0x401E000000000000 執行結果解釋:
這個值在記憶體中的儲存形式就是圖5的形式,所以用%f打印出來就是7.5.