printf("%d",5.01)和printf("%f",5)的輸出結果

原文地址:http://blog.csdn.net/yahohi/article/details/7701434

  1. printf("%f\n",5);  
  2. printf("%d\n",5.01);   
  3. printf("%f\n", (float)5);    
  4. printf("%f\n", 5.f);    


輸出結果:

看到結果,會感覺非常奇怪。1處怎麼會輸出0呢?2又爲何會顯示這麼大的一個數呢?

解釋:

下面是轉自網上的一篇博客的解釋

1,之所以沒輸出5,這是C語言設計的原因。
2,之所以輸出0,這是計算機體系結構的問題。

具體來說:

printf函數不會進行任何類型轉換,它只是從內存中讀出你所提供的元素的值(按照%d,%f等控制字符提示的格式)。C語言設計中,int類型一般是32bit或者16bit,而float一般是64bit,並且有可能使用科學計數保存。這點就和huhugo88所說一樣,5在內存中爲00000000,00000101。而且5一般都在靜態區,程序的靜態存儲區默認是0,那麼當用%f來讀時,就會讀64bit,也就是會讀之前的很多位0,最後按照(有效數字)×(基數2)pow(指數)的方式來取數,自然結果是0

之所以Vc中不允許這種情況,而有些編譯器就允許這麼輸出就是編譯器設置的問題。按理說,這樣訪問內存是屬於越界訪問,應該禁止。不過只是讀,傷害性不大而已。  對於單精度浮點數(32bit),不少c語言編譯系統以24位表示小數部分(包括1bit符號位),以8位表示指數部分。 ==========================printf("%d\n",5.01);  爲什麼輸出一個大數?在講這個題目之前,預備知識,講一下,printf函數,輸入參數是讀入緩衝區保存,再按照%?的格式從緩衝區中讀出數據,並據此格式解釋數據。

有了這個知識之後,在講程序員面試寶典上看到一個題:

 

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
    printf("%d\n",5.01);   
return 0;   
}   
輸出結果爲:188978561  
然後開始研究爲什麼會是這個數?

5.01是double類型,內存中佔8個字節,保存在緩衝區。而%d爲整型,佔4個字節,printf從緩衝區中讀入4字節,先讀到低32位的數據。也就是說printf輸出的應該是5.01以double類型保存數劇的低32位。爲了檢驗此結果是否正確,對比5.01在內存中的表示與輸出。

 

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
double d = 5.01;   
int *p = (int *)(&d);   
int rst = 1889785610;   
    printf("1).%x\n",*p);   
    printf("2).%x\n",*(p+1));   
    printf("3).%x\n",rst);   
return 0;   
}   
輸出爲:   
1).0x70a3d70a   
2).0x40140a3d   
3).0x70a3d70a  
這也就證明了%d輸出了5.01的低32低。5.01的double類型,在內存的的表示爲0x40140a3d70a3d70a。

事情看似也就完成了。

我又想,如果輸入是浮點類型的5.01f,又會發生什麼呢?

 

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
float f = 5.01f;     
int *p = (int *)(&f);        
    printf("1).0x%x\n",*p);     
    printf("2).0x%x\n",5.01f);     
return 0;     
}   
輸出:   
1).0x40a051ec   
2).0x80000000  
我們發現,此時輸出的並不是浮點類型5.01f的內存的表示,這是爲什麼呢?

然後看到一個說法,是printf會把%f按double類型輸出,也就是說會把參數float型的轉成double型在輸出。

但現在並不是%f,當然用%f顯示的是正確的結果。於是我猜測,printf是將所在float型讀入的數據都自動的轉化爲double型了,然後%f就按double處理,而我們這是%d,所以顯示的爲float轉化爲double型後的低4字節。

驗證此想法:

 

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
double f = 5.01;     
int *p = (int *)(&f);   
    printf("1).0x%x\n",*p);   
    printf("2).0x%x\n",*(p+1));     
    printf("3).0x%x\n",5.01f);     
return 0;     
}   
輸出:   
1).0x70a3d70a   
2).0x40140a3d   
3).0x80000000  
但是我們發現結果並不一樣,於是我又猜想,也是許printf將float轉化爲double的方式與默認的方式不一樣

5.01d的默認的表示爲:0x40140a3d70a3d70a,在上面已經說明了

 

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
    printf("0x%8x\n0x%8x\n",5.01f);   
return 0;   
}   
輸出爲:   
0x80000000   
0x40140a3d  
與是發現printf將5.01f->5.01d的表示是:0x40140a3d80000000

接着就是看這兩個值是否都是爲5.01了:

view plaincopy to clipboardprint?

#include "stdio.h" 
int main(int argc, char* argv[])   
{   
int d1[2], d2[2];   
    d1[0]=0x80000000;   
    d1[1]=0x40140a3d;   
    d2[0]=0x70a3d70a;   
    d2[1]=0x40140a3d;   
double *p1 = (double *)d1;   
double *p2 = (double *)d2;   
    printf("1).%f\n",*p1);   
    printf("2).%f\n",*p2);   
return 0;     
}   
輸出爲:   
1).5.010000   
2).5.010000  
也就證明了0x40140a3d80000000,與0x40140a3d70a3d70a都是5.01d在機器中的表示。前者爲5.01f(0x40a051ec)由printf轉化爲double後的表示,後者爲5.01d的默認的表示。

總結:printf將輸的浮點型參數全都自動轉化爲雙精度型,且與默認的雙精度的表示方法是不同的。最重要一點,printf不安全,類型不安全,要是類型不對了,也許我們就掛了^_^


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/fengyunjh/archive/2011/03/07/6230164.aspx

 

=======================================================================================================================

通過以上解釋,我們大致明白:

1. printf輸出float型時,會自動轉化成double型;

2. 由於存儲時,都是先低位,再高位,同時經過轉化成double,前面會取很多0(越界訪問);

3. 5.01,打印時按照int來取,只取前四個字節。


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