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

나는 우분투에서 gcc으로 프로그램을 실행 중이다. values-- 무슨 일이 일어나고 무엇플로팅 타입의 정수 값 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 함수는 전달 된 형식의 형식을 알지 못합니다. 그 부분은 가변적이기 때문입니다. float 지나가는 C 표준에서

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

는 자동 double (C11§6.5.2.2/6)로 승급되고, 아무것도 호출자 측에서 수행되지 않는다.

printf 내부에는 ... 거지 (§6.7.6.3/9)의 유형을 알지 못하므로 형식 문자열 인 다른 곳에서 힌트를 사용해야합니다. "%d"을 전달 했으므로 int이 필요하다고 함수에 알립니다.

C 표준에 따르면, 이것은 이상한 숫자, 이야기의 끝을 인쇄 할 가능성을 포함하는 정의되지 않은 동작 (§7.21.6.1/8-9)을 초래합니다.

하지만 실제로 무엇이 일어나고 있습니까? 대부분의 플랫폼에서 double은 "IEEE 754 binary64"형식으로 표시되고 floatbinary32 형식으로 표시됩니다. 당신이 입력 한 숫자는 숫자는 다음과 같이 근사 할 것입니다 의미만을 의미의 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"을 삽입해야합니다. 나는이가 ± m × 2 전자로 분해 방식을 볼 수 http://www.binaryconvert.com/convert_double.html를 사용하는 것이 좋습니다

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

:

3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹ 

는 이들은 그 숫자의 실제 표현을 유도하기 위해 주로 형식 어쨌든

, 당신의 시스템이 little-endian format를 사용하여 메모리에 배치되어있는 번호를 의미 일반 설정에서의 x86/x86_64의/ARM, 가정하자, 이렇게 전달 된 인수는 될 것 같은 printf 내부

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) 

, 그것을 파싱 형식 문자열 "%d"을 소비하고 그리고, int가 SO 4 바이트가 가변 입력에서 촬영되고 있기 때문에 % D의 필요하다는 것을 알게되는 :에서 printf

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

그래서 0x60000000을 받고 10 진수 정수로 표시합니다 (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

+0

완벽을 기하기 위해 [Two 's Complement - Wikipedia] (http://en.wikipedia.org/wiki/Two%27s_complement)와 [Two 's Complement notes Thomas Finley] (http : // tfinley.net/notes/cps104/twoscomp.html). – mctylr

2

지금까지 게시 된 다른 답변에 요점이 누락되었다고 가정합니다. 사용자가 고의적으로을 스캔 및 인쇄를 위해 다른 변환을 사용하여 결과를 이해하고 있다고 생각합니다. 실제로, 당신이 방금 실수를했다면, 당신은 내 대답을 무시할 수 있습니다.

기본적으로 부동 소수점 숫자의 비트 패턴이 정의되는 방식을 설명하는 this article을 읽고 그 숫자 각각에 대해 비트 패턴을 작성해야합니다. 정수가 저장되는 방식을 이해했다면 답을 얻어야합니다.

0

두 번째 printf 문에 사용하는 d 변환 지정자에는 int 유형의 인수가 필요합니다. C 기본 인수 수준 올리기 후 a 인수는 double입니다. 예상했던 것과 다른 유형의 인수를 정의되지 않은 동작으로 전달하고 정의되지 않은 동작이있는 일반적인 경우처럼 아무 것도 발생할 수 있습니다.

0

정확히 무슨 일이 일어나는지 알고 싶다면 printf("%d",a); 대신 printf('0x%08x\n', a);을 시도하십시오. printf("%d",a);이 제공하는 대신 변수 a의 실제 비트를 볼 수 있습니다.

0

간단히 말해서 printf ("%d",a); : a의 메모리가 int라고 생각하여 내용을 int로 해석합니다. 및 printf("%f",a);은 a의 메모리 내용을 실제로 플로트로 간주합니다 ...

printf("%d",(int)a); // a는 int로 변환됩니다 (잘라내기로 by (int) cast). 따라서 approximativ 값은 a입니다.

0

C에서 float는 인수가 가변적 인 함수의 인수로 전달되어 double로 승격됩니다. 그래서 printf 함수의 형식 문자열을 참조 할 때 float 및 double 형식에 다른 형식 지정자가 표시되지 않습니다. 따라서 "a"는 printf에 전달 될 때 32 비트 float에서 64 비트 double로 변환됩니다. 64 비트 중 32 비트가 0이되는 방식으로 4와 5가 double 형으로 표현되는데,이 제로 비트는 printf 함수가 정수로 해석하기 때문에 정수로 출력하기 때문에 .

0

printf()은 첫 번째 매개 변수에서 언급 한 형식 지정자를 사용하여 가변 길이 인수를 해석합니다. printf()의 서명은 다음과 같습니다.

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

그래서 printf() 코드는 아마이 stdarg.h를 사용하여 같은 기록 될 것입니다. 당신이 float에 대한 %d을 전달하는 경우

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); 
} 

그럼 당신은 printf() 내부에 무슨 일이 일어날 지 알아낼 수 있습니다.printf()float 변수를 int으로 해석하며이 동작은 정의되지 않습니다.

희망이 도움이됩니다.