1. 程式人生 > >C語言中printf用%d輸出float型別資料,或以%f輸出int型資料的結果

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標準
(1)單精度(32位)浮點數的結構:
圖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


圖9 圖9和圖1的輸出結果統一起來,圖1中以float型別儲存的1.23,與以double型別儲存的1.23結果不同,就是以上的解釋;還有一點,程式中用%d列印1.23出來遺傳很長的數字, 從圖9可以看出,因為記憶體的低32為不為0,所以才會有很長一串數字,這是因為1.23沒法精確表示,只能近似表示導致位數過多。
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.

3.總結

(1)一般能被精確表示的浮點數(單精度或者雙精度),用printf(“%d”)列印的結果一般為0; (2)不能被精確表示的付點是(單精度或者雙精度),用printf(“%d”)列印的結果是一長串數,一般是近似尾數。 (3)整形型別的數,用printf(“%f”)打印出來的結果一般為0; (4)long long 型別的很大的數,用printf(“%f”)打印出來可能為一個小數; (5)注意,folat輸出6位小數,7位有效位;double 輸出6位小數,有效位數為16位,超出有效位,運算時候會出錯。 附幾個轉碼工具連結: