¿Cómo funciona este programa?

88
#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", a);
    return 0;
}

Muestra un 0!! ¿Cómo es eso posible? Cual es el razonamiento?


Deliberadamente he puesto un %den la printfdeclaración para estudiar el comportamiento de printf.

Lazer
fuente

Respuestas:

239

Eso es porque %despera un intpero le proporcionaste un flotador.

Utilice %e/ %f/ %gpara imprimir el flotador.


Por qué se imprime 0: el número de coma flotante se convierte a doubleantes de enviar a printf. El número 1234.5 en representación doble en little endian es

00 00 00 00  00 4A 93 40

A %dconsume un entero de 32 bits, por lo que se imprime un cero. (Como prueba, printf("%d, %d\n", 1234.5f);podría obtener la salida 0, 1083394560).


En cuanto a por qué floatse convierte a double, como es el prototipo de printf int printf(const char*, ...), desde 6.5.2.2/7,

La notación de puntos suspensivos en un declarador de prototipo de función hace que la conversión del tipo de argumento se detenga después del último parámetro declarado. Las promociones de argumentos predeterminadas se realizan en argumentos finales.

y de 6.5.2.2/6,

Si la expresión que denota la función llamada tiene un tipo que no incluye un prototipo, las promociones de enteros se realizan en cada argumento y floatse promueven los argumentos que tienen tipo double. Estos se denominan promociones de argumentos predeterminados .

(Gracias Alok por descubrir esto).

Kennytm
fuente
4
+1 Mejor respuesta. Responde tanto "estándar técnicamente correcto" por qué y "su posible implementación" por qué.
Chris Lutz
12
Creo que eres la única de las 12 personas que realmente proporcionó la respuesta que estaba buscando.
Gabe
11
Porque printfes una función variadica, y el estándar dice que para funciones variadas, a floatse convierte a doubleantes de pasar.
Alok Singhal
8
Del estándar C: "La notación de puntos suspensivos en un declarador de prototipo de función hace que la conversión del tipo de argumento se detenga después del último parámetro declarado. Las promociones de argumentos predeterminadas se realizan en los argumentos finales". y "... y los argumentos que tienen el tipo float se promueven al doble. Estos se denominan promociones de argumentos predeterminados ".
Alok Singhal
2
El uso de un especificador de formato incorrecto en printf()invoca un comportamiento indefinido .
Prasoon Saurav
45

Técnicamente hablando no existe el printf , cada biblioteca implementa la suya propia y, por lo tanto, su método para tratar de estudiar printfel comportamiento haciendo lo que está haciendo no será de mucha utilidad. Podría estar intentando estudiar el comportamiento de printfen su sistema, y ​​si es así, debería leer la documentación y mirar el código fuente para ver printfsi está disponible para su biblioteca.

Por ejemplo, en mi Macbook, obtengo la salida 1606416304con su programa.

Habiendo dicho eso, cuando pasas a floata una función variada, floatse pasa como a double. Entonces, su programa equivale a haber declarado acomo double.

Para examinar los bytes de a double, puede ver esta respuesta a una pregunta reciente aquí en SO.

Vamos a hacer eso:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    unsigned char *p = (unsigned char *)&a;
    size_t i;

    printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
    for (i=0; i < sizeof a; ++i)
        printf("%02x ", p[i]);
    putchar('\n');
    return 0;
}

Cuando ejecuto el programa anterior, obtengo:

size of double: 8, int: 4
00 00 00 00 00 4a 93 40 

Entonces, los primeros cuatro bytes doubleresultaron ser 0, que puede ser la razón por la que obtuviste 0como resultado de tu printfllamada.

Para obtener resultados más interesantes, podemos cambiar un poco el programa:

#include <stdio.h>

int main(void)
{
    double a = 1234.5f;
    int b = 42;

    printf("%d %d\n", a, b);
    return 0;
}

Cuando ejecuto el programa anterior en mi Macbook, obtengo:

42 1606416384

Con el mismo programa en una máquina Linux, obtengo:

0 1083394560
Alok Singhal
fuente
¿Por qué programó la impresión al revés? ¿Me estoy perdiendo de algo? Si b es un int = 42 y '% d' es el formato de número entero, ¿por qué no es el segundo valor impreso, ya que es la segunda variable en los argumentos de printf? ¿Es esto un error tipográfico?
Viaje Katastic
Probablemente porque los intargumentos se pasan en registros diferentes a los doubleargumentos. printfwith %dtoma el intargumento que es 42 y el segundo %dprobablemente imprime basura ya que no había un segundo intargumento.
Alok Singhal
20

El %despecificador dice printfque espere un número entero. Entonces, los primeros cuatro (o dos, dependiendo de la plataforma) bytes del flotante se interpretan como un número entero. Si resultan ser cero, se imprime un cero

La representación binaria de 1234.5 es algo así como

1.00110100101 * 2^10 (exponent is decimal ...)

Con un compilador de C que representa en floatrealidad como valores dobles IEEE754, los bytes serían (si no me equivoco)

01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000

En un sistema Intel (x86) con poca endianess (es decir, el byte menos significativo viene primero), esta secuencia de bytes se invierte para que los primeros cuatro bytes sean cero. Es decir, lo que printfimprime ...

Consulte este artículo de Wikipedia para ver la representación de punto flotante de acuerdo con IEEE754.

MartinStettner
fuente
7

Es por la representación de un flotador en binario. La conversión a un número entero lo deja con 0.

Shaihi
fuente
1
Pareces ser el único que entendió lo que estaba pidiendo. A menos que me equivoque, por supuesto.
Mizipzor
Estoy de acuerdo en que la respuesta es inexacta e incompleta, pero no incorrecta.
Shaihi
7

Porque invocó un comportamiento indefinido: violó el contrato del método printf () mintiéndole sobre sus tipos de parámetros, por lo que el compilador es libre de hacer lo que le plazca. Podría hacer que la salida del programa "dksjalk es un tonto!" y técnicamente aún estaría bien.

Kilian Foth
fuente
5

La razón es que printf()es una función bastante tonta. No comprueba los tipos en absoluto. Si dice que el primer argumento es un int(y esto es lo que está diciendo %d), le cree y solo toma los bytes necesarios para un int. En este caso, asumiendo que su máquina usa cuatro bytes inty ocho bytes double(el floatse convierte en un doubleinterior printf()), los primeros cuatro bytes aserán solo ceros, y esto se imprime.

Gorpik
fuente
3

No convertirá automáticamente flotante en entero. Porque ambos tienen diferente formato de almacenamiento. Entonces, si desea convertir, use (int) typecasting.

#include <stdio.h>

int main() {
    float a = 1234.5f;
    printf("%d\n", (int)a);
    return 0;
}
sganesh
fuente
2

Dado que también lo etiquetó con C ++, este código realiza la conversión como probablemente espera:

#include <iostream.h>

int main() {
    float a = 1234.5f;
    std::cout << a << " " << (int)a << "\n";
    return 0;
}

Salida:

1234.5 1234
Mizipzor
fuente
1

%d es decimal

%f es flotar

vea más de estos aquí .

Obtienes 0 porque los números flotantes y enteros se representan de manera diferente.

Filip Ekberg
fuente
1

Solo necesita usar el especificador de formato apropiado (% d,% f,% s, etc.) con el tipo de datos relevante (int, float, string, etc.).

Muhammad Maqsoodur Rehman
fuente
La pregunta no es cómo solucionarlo, sino por qué funciona como funciona.
ya23
0

Quieres% f no% d

SC Madsen
fuente
0

oye, tenía que imprimir algo así que imprimió un 0. ¡Recuerda que en C 0 está todo lo demás!

chunkyguy
fuente
1
¿Cómo es eso? En C no existe todo lo demás.
Vlad
si x es algo, entonces! x == 0 :)
chunkyguy
if (iGot == "todo") imprime "todo"; si no, imprime "nada";
nik
0

No es un número entero. Intente usar %f.

tangrs
fuente
Supongo que la pregunta es, ¿por qué no convierte el flotador en int y muestra "1234"?
Björn Pollex
no espere que c no le permita hacer algo que no tiene sentido lógico. Sí, muchos lenguajes darían el 1234 que puede esperar, y tal vez incluso algunas implementaciones de c no creo que este comportamiento esté definido. C te permite ahorcarte, es como el padre que te deja probar el crack por el placer de hacerlo.
repetición
Porque C está diseñado para ser flexible.
Tangrs