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

Estoy ejecutando el programa con gcc en Ubuntu. Para values--Valor entero de tipo flotante en 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 

¿Qué está pasando? ¿Por qué se imprimen estos valores? Lo hago intencionalmente, pero me gustaría entender por qué sucede esto. Los detalles son apreciados!

Respuesta

22

La función printf no conoce el tipo de formato que pasó, porque esa parte es variadica.

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

En el estándar C, pasando por una float serán promovidos automáticamente a un double (C11§6.5.2.2/6), y nada más se llevará a cabo en el lado del llamante.

Dentro de printf, ya que no conoce el tipo de ese ... thingie (§6.7.6.3/9), tiene que usar la sugerencia de otro lugar - la cadena de formato. Desde que pasó el "%d", le dice a la función que se espera un int.

De acuerdo con el estándar C, esto conduce a un comportamiento indefinido (§7.21.6.1/8-9), que incluye la posibilidad de imprimir un número extraño, al final de la historia.

Pero, ¿qué está pasando realmente? En la mayoría de las plataformas, un double se representa como formato "IEEE 754 binary64" y un float en formato binary32. Los números que ha introducido se convierten en un flotador, que sólo tiene 23 bits de importancia, lo que significa que los números se aproximan así:

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) 

Ahora nos convertimos esta duplicar, que tiene 53 bits de significación , que tenemos que insertar 30 "0" binarios al final de estos números, para producir, por ejemplo,

3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹ 

Estos son principalmente para derivar la representación real de esos números, que son:

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

recomiendo el uso de http://www.binaryconvert.com/convert_double.html para ver cómo esto se descompone al ± m × 2 correo formato.

De todos modos, supongo que su sistema es un sistema x86/x86_64/ARM en un entorno normal, lo que significa que los números se establecen en la memoria utilizando little-endian format, por lo que los argumentos que se pasan a ser como

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) 

Dentro de la printf , que consume la cadena de formato "%d", lo procesa, y luego se entera de que un int es necesaria debido a% d, por lo que 4 bytes se toman de la entrada variadic, que es:

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

así, printf recibirá 0x60000000 y lo mostrará como un número entero decimal, que es 1610612736, por lo que puede ver ese resultado. Los otros números se pueden explicar de manera similar.

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 por tomarse el tiempo para escribir todo esto. :) – Mysticial

+0

Para completar, un par de enlaces de complementos de Dos útiles, [Complemento de dos - Wikipedia] (http://en.wikipedia.org/wiki/Two%27s_complement) y [Dos notas de complemento Thomas Finley] (http: // tfinley.net/notes/cps104/twoscomp.html). – mctylr

2

Supongo que las otras respuestas publicadas hasta el momento no tienen sentido: creo que deliberadamente usando diferentes conversiones para escanear e imprimir, y quiero entender los resultados. Si, de hecho, acaba de cometer un error, puede ignorar mi respuesta.

Básicamente, debe leer this article, que explicará cómo se definen los patrones de bits para números de coma flotante y luego escribirá los patrones de bits para cada uno de esos números. Dado que usted entiende cómo se almacenan los enteros, entonces debe tener sus respuestas.

0

El especificador de conversión d que utiliza en su segunda declaración printf requiere un argumento de tipo int. Su argumento a después de la promoción argumento por defecto es de tipo C double. Al pasar un argumento de un tipo diferente que el que se espera es un comportamiento indefinido y como de costumbre, con un comportamiento indefinido, cualquier cosa puede suceder.

0

Si quiere saber exactamente qué está pasando, intente printf('0x%08x\n', a); en lugar de printf("%d",a);. Podrás ver los bits reales de la variable de un lugar de lo printf("%d",a); te da.

0

simplemente porque printf ("%d",a);: piensan que la memoria en un es un int por lo que interpretar su contenido como un int. y printf("%f",a); considere el contenido de la memoria de a como un flotador que es realmente ...

pero si escribe printf("%d",(int)a); // a se transforma en un int (por (int) cast con truncamiento). por lo que se imprime el valor aproximado de a.

0

En C, los flotadores se pasan como argumentos de funciones con un número variable de argumentos son ascendidos a dobles. Es por eso que en la referencia de la cadena de formato de la función printf no verá diferentes especificadores de formato para flotantes y para dobles. Por lo tanto, su "a" se convierte de un flotante de 32 bits a un doble de 64 bits cuando se pasa a printf. Ocurre que 4 y 5 se representan como dobles de tal manera que 32 de los 64 bits son ceros, y estos bits cero son los que la función printf interpreta como un entero, ya que usted le indicó que imprima un número entero .

0

printf() interpreta su (s) argumento (s) de longitud variable utilizando los especificadores de formato que se han mencionado en el primer parámetro. La firma de printf() es la siguiente.

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

Así que el código printf() probablemente se escribe así el uso de 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); 
} 

Así que si pasa por un %dfloat entonces se puede imaginar lo que sucederá en el interior del printf().El printf() interpretará una variable float como int y este comportamiento no está definido.

Espero que esto ayude!

Cuestiones relacionadas