2011-12-29 172 views
10
#include<stdio.h> 
int main() 
{ 
    float a; 
    printf("Enter a number:"); 
    scanf("%f",&a); 
    printf("%d",a); 
    return 0; 
} 

我在Ubuntu中運行程序gcc。 對於數值 -C中浮點類型的整數值

  3.3 it gives value 1610612736 
      3.4 it gives value 1073741824 
      3.5 it gives value 0 
      3.6 it gives value -1073741824 
      4 it gives value 0 
      5 it gives value 0 

發生了什麼事?爲什麼打印這些值?我故意這樣做,但想知道爲什麼會發生這種情況。細節非常感謝!

回答

22

printf函數不知道您傳入的格式類型,因爲該部分是可變參數。

int printf(const char* format, ...); 
//        ^^^ 

在C標準,傳遞一個float會自動提升爲double(C11§6.5.2.2/ 6),而不是其他將在發送方完成。

printf裏面,因爲它不知道... thingie(§6.7.6.3/ 9)的類型,所以它必須使用其他地方的提示 - 格式字符串。既然你已經通過了"%d",它正在告訴函數,預計有一個int

根據C標準,這導致了未定義的行爲(§7.21.6.1/ 8-9),其中包括打印一些奇怪的數字,故事結束的可能性。

但是究竟發生了什麼?在大多數平臺中,double被表示爲「IEEE 754 binary64」格式,並且binary32格式表示float。您所輸入的號碼將被轉換爲浮動,只有具有意義的23位,這意味着數字將這樣的近似:

3.3 ~ (0b1.10100110011001100110011) × 2¹ (actually: 3.2999999523162842...) 
3.4 ~ (0b1.10110011001100110011010) × 2¹ (actually: 3.4000000953674316...) 
3.5 = (0b1.11     ) × 2¹ (actually: 3.5) 
3.6 ~ (0b1.11001100110011001100110) × 2¹ (actually: 3.5999999046325684...) 
4 = (0b1      ) × 2² (actually: 4) 
5 = (0b1.01     ) × 2² (actually: 5) 

現在我們將它轉​​換爲加倍,它具有意義的53位,我們必須在這些數字的末尾插入30個二進制「0」,以產生例如

3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹ 

這些主要是推導這些數字,它們是實際表示:

3.3 → 400A6666 60000000 
3.4 → 400B3333 40000000 
3.5 → 400C0000 00000000 
3.6 → 400CCCCC C0000000 
4 → 40100000 00000000 
5 → 40140000 00000000 

我建議使用http://www.binaryconvert.com/convert_double.html看不出這分解到±×2 ë格式。

不管怎樣,我想你的系統是在正常設置在x86/x86_64的/ ARM,這意味着數字使用little-endian format在內存中擺出來,所以傳遞的參數會像

byte 
    #0 #1 ...   #4 ...   #8 .... 
+----+----+----+----+ +----+----+----+----+----+----+----+----+ 
| 08 | 10 | 02 | 00 | | 00 | 00 | 00 | 60 | 66 | 66 | 0A | 40 | .... 
+----+----+----+----+ +----+----+----+----+----+----+----+----+ 
address of "%d"   content of 3.299999952316284 
(just an example) 

裏面的printf ,它消耗格式字符串"%d",解析它,然後找出該一個int需要,因爲%d,所以4個字節從可變參數輸入拍攝,這是:

byte 
    #0 #1 ...   #4 ...   #8 .... 
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+ 
: 08 : 10 : 02 : 00 : | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : .... 
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+ 
address of "%d"  ~~~~~~~~~~~~~~~~~~~ 
         this, as an 'int' 

所以,printf的將收到0x60000000,並將其顯示爲十六進制整數,即1610612736,這就是您看到該結果的原因。其他數字可以類似解釋。

3.3 → ... 60000000 = 1610612736 
3.4 → ... 40000000 = 1073741824 
3.5 → ... 00000000 = 0 
3.6 → ... C0000000 = -1073741824 (note 2's complement) 
4 → ... 00000000 = 0 
5 → ... 00000000 = 0 
+0

+1需要時間寫出所有這些。 :) – Mysticial 2011-12-29 19:29:27

+0

爲了完整起見,有兩個有用的補碼鏈接,[Two's Complement - Wikipedia](http://en.wikipedia.org/wiki/Two%27s_complement)和[Two's Complement notes Thomas Finley](http:// tfinley。淨/筆記/ cps104/twoscomp.html)。 – mctylr 2011-12-29 20:04:43

2

我假設現在發佈的其他答案缺少重點:我認爲你故意使用不同的轉換進行掃描和打印,並且想要了解結果。如果你確實犯了一個錯誤,那麼你可以忽略我的答案。

基本上,你需要閱讀this article,這將解釋如何定義浮點數的位模式,然後寫出每個數字的位模式。鑑於你瞭解整數如何存儲,你應該得到你的答案。

0

d您在第二個printf語句中使用的轉換說明符需要參數類型爲int。在C默認參數提升後,您的參數a的類型爲double。傳遞一個不同類型的參數是一個未定義的行爲,並且像往常一樣未定義的行爲,任何事情都可能發生。

0

如果您想知道到底發生了什麼,請嘗試printf('0x%08x\n', a);而不是printf("%d",a);。你將能夠看到變量a的實際位,而不是printf("%d",a);給你的東西。

0

只是因爲printf ("%d",a);:認爲a中的內存是一個int,因此它將其內容解釋爲int。 和printf("%f",a);考慮一個內存的內存爲一個浮動它是真的...

但如果你寫printf("%d",(int)a); // a被轉換爲int(通過(int)強制轉換)。所以打印出a的近似值。

0

在C中,浮點數作爲參數傳遞給具有可變數量的參數的函數被提升爲雙精度。這就是爲什麼在printf函數的格式字符串的引用中,您不會看到浮點數和雙精度的不同格式說明符。所以,當傳遞給printf時,你的「a」從32位浮點數轉換爲64位雙精度浮點數。恰巧4和5被表示爲雙精度,64位中有32位是零,而這些零位是printf函數解釋爲整數的那些位,因爲您告訴它打印一個整數。

0

printf()使用第一個參數中提到的格式說明符來解釋其可變長度參數。 printf()的簽名如下。

int printf(const char *format, ...); 

所以printf()代碼可能會被寫成這樣使用stdarg.h

int printf(const char *format, ...) { 
    va_list ap; 
    char *p, *sval; 
    int ival; 
    float fval; 

    va_start(ap, format); 
    for(p=format; *p ; p++) { 
     if (*p != '%') { 
      putchar(*p); 
      continue; 
     } 
     switch(*++p) { 
      case 'd': 
       ival = va_arg(ap, int); 
       break; 

      case 'f': 
       fval = va_arg(ap, float); 
       break; 

      case 's': 
       for (sval = va_arg(ap, char *); *sval; sval++); 
       break; 

      default: 
       putchar(*p); 
       break; 
     } 
    } 
    va_end(ap); 
} 

所以,如果你通過%dfloat那麼你可以找出printf()裏面會發生什麼。printf()將把float變量解釋爲int,並且此行爲未定義!

希望這會有所幫助!