Entendiendo las conversiones implícitas para printf

El estándar C99 diferencia entre conversiones de tipo implícitas y explícitas (6.3 conversiones). Supongo, pero no se pudo encontrar, que se realizan conversiones implícitas, cuando el tipo de destino es de mayor precisión que la fuente y puede representar su valor. [Eso es lo que considero que pasa de INT a DOBLE]. Dado eso, miro el siguiente ejemplo:

#include  // printf #include  // for INT_MIN #include  // for endianess #define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100) int main() { printf("sizeof(int): %lu\n", sizeof(int)); printf("sizeof(float): %lu\n", sizeof(float)); printf("sizeof(double): %lu\n", sizeof(double)); printf( IS_BIG_ENDIAN == 1 ? "Big" : "Little" ); printf( " Endian\n" ); int a = INT_MIN; printf("INT_MIN: %i\n", a); printf("INT_MIN as double (or float?): %e\n", a); } 

Me sorprendió mucho encontrar esa salida:

 sizeof(int): 4 sizeof(float): 4 sizeof(double): 8 Little Endian INT_MIN: -2147483648 INT_MIN as double (or float?): 6.916919e-323 

Por lo tanto, el valor flotante impreso es un número de punto flotante subnormal cerca del mínimo positivo subnormal positivo doble 4.9406564584124654 × 10 ^ −324. Suceden cosas extrañas cuando comento los dos printf para endianess, obtengo otro valor para el doble:

 #include  // printf #include  // for INT_MIN #include  // for endianess #define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100) int main() { printf("sizeof(int): %lu\n", sizeof(int)); printf("sizeof(float): %lu\n", sizeof(float)); printf("sizeof(double): %lu\n", sizeof(double)); // printf( IS_BIG_ENDIAN == 1 ? "Big" : "Little" ); printf( " Endian\n" ); int a = INT_MIN; printf("INT_MIN: %i\n", a); printf("INT_MIN as double (or float?): %e\n", a); } 

salida:

 sizeof(int): 4 sizeof(float): 4 sizeof(double): 8 INT_MIN: -2147483648 INT_MIN as double (or float?): 4.940656e-324 
  • gcc –version: (Ubuntu 4.8.2-19ubuntu1) 4.8.2
  • uname: x86_64 GNU / Linux
  • opciones del comstackdor donde: gcc -ox xc -Wall -Wextra -std = c99 –pedantic
  • Y sí, donde hay una advertencia:
 xc: In function 'main': xc:15:3: warning: format '%e' expects argument of type 'double', but argument 2 has type 'int' [-Wformat=] printf("INT_MIN as double (or float?): %e\n", a); ^ 

Pero todavía no puedo entender lo que está sucediendo exactamente.

  • en little endianess considero MIN_INT como: 00 … 0001 y MIN_DBL (Subnormal) como 100..00 #, comenzando con la mantisa, seguido del exponente y concluyo con el # como bit de signo.
  • ¿Esta forma de aplicar el especificador de formato “% e” en un int, es una conversión implícita ?, ¿una conversión de reinterpretación?

Estoy perdido, por favor ilumíname.

 printf("INT_MIN as double (or float?): %e\n", a); 

La línea anterior tiene un problema No puede usar %e para imprimir letras. El comportamiento es indefinido.

Deberías usar

 printf("INT_MIN as double (or float?): %e\n", (double)a); 

o

 double t = a; printf("INT_MIN as double (or float?): %e\n", t); 

Publicación relacionada : esta publicación explica cómo el uso de especificadores de impresión incorrectos en printf puede llevar a UB.

Los argumentos para va_arg funciones va_arg no se convierten, sintácticamente el comstackdor no sabe nada sobre los argumentos para tales funciones, por lo que no puede hacer eso. Sin embargo, los comstackdores modernos saben interpretar la cadena de formato, por lo que son capaces de advertirle cuando ocurre algo sospechoso. Eso es lo que sucede cuando ves la advertencia de gcc.

Para ser más precisos, hay algunas promociones que se realizan para tipos de enteros estrechos , se promueven a int , y para float se promueve a double . Pero eso es todo lo mágico que puede pasar aquí.

En resumen, utilice siempre el especificador de formato correcto.

Por cierto, para size_t partir de su sizeof expresiones, la correcta es %zu .