C: Lanzar un entero mínimo de 32 bits (-2147483648) para flotar da un número positivo (2147483648.0)

Estaba trabajando en un proyecto integrado cuando me encontré con algo que pensé que era un comportamiento extraño. Pude reproducirlo en el teclado (ver a continuación) para confirmar, pero no tengo ningún otro comstackdor de C en mi máquina para probarlo.

Escenario: tengo #define para el valor más negativo que puede contener un entero de 32 bits, y luego trato de usar esto para compararlo con un valor de coma flotante como se muestra a continuación:

 #define INT32_MIN (-2147483648L) void main() { float myNumber = 0.0f; if(myNumber > INT32_MIN) { printf("Everything is OK"); } else { printf("The universe is broken!!"); } } 

Enlace del teclado numérico: http://codepad.org/cBneMZL5

Para mí, parece que este código debería funcionar bien, pero para mi sorpresa se imprime ¡ The universe is broken!! .

Este código implícitamente arroja el INT32_MIN a un float , pero resulta que esto da como resultado un valor de coma flotante de 2147483648.0 (¡positivo!), Aunque el tipo de punto flotante es perfectamente capaz de representar -2147483648.0 .

¿Alguien tiene alguna idea de la causa de este comportamiento?

SOLUCIÓN DE CÓDIGO : Como Steve Jessop mencionó en su respuesta, limits.h y stdint.h contienen correctamente (trabajo) int range define s, por lo que ahora estoy usando estos en lugar de mi propio #define

RESUMEN DE EXPLICACIÓN DE PROBLEMAS / SOLUCIONES: Dadas las respuestas y las discusiones, creo que este es un buen resumen de lo que está sucediendo (nota: sigue leyendo las respuestas / comentarios porque brindan una explicación más detallada):

  • Estoy usando un comstackdor C89 con longs de 32 bits, por lo que cualquier valor mayor que LONG_MAX y menor o igual que ULONG_MAX seguido por el postfijo L tiene un tipo de unsigned long
  • (-2147483648L) es realmente unario - en un valor unsigned long (ver punto anterior): -(2147483648L) . Esta operación de negación ‘ajusta’ el valor alrededor de ser el valor unsigned long de 2147483648 (porque los unsigned long 32 bits tienen el rango 04294967295 ).
  • Este número unsigned long parece al valor int negativo esperado cuando se imprime como un int o se pasa a una función porque primero se lanza a un int , que está ajustando este fuera de rango 2147483648 a -2147483648 (porque 32 -bit int s tienen el rango -2147483648 a 2147483647)
  • Sin embargo, el elenco para float utiliza el valor real unsigned long 2147483648 para la conversión, lo que da como resultado el valor en coma flotante de 2147483648.0 .

En C89 con una long 32 bits, 2147483648L tiene el tipo unsigned long int (consulte 3.1.3.2 Constantes de enteros). Entonces, una vez que se ha aplicado la aritmética de módulo a la operación unaria menos, INT32_MIN es el valor positivo 2147483648 con el tipo unsigned long .

En C99, 2147483648L tiene un tipo long si long es mayor que 32 bits, o long long si no (ver 6.4.4.1 Constantes integer). Entonces, no hay ningún problema e INT32_MIN es el valor negativo -2147483648 con tipo long o long long .

De forma similar, en C89 con long mayores de 32 bits, 2147483648L tiene un tipo long e INT32_MIN es negativo.

Supongo que estás usando un comstackdor C89 con una long 32 bits.

Una forma de verlo es que C99 soluciona un “error” en C89. En C99, un literal decimal sin sufijo U siempre tiene el tipo firmado, mientras que en C89 puede estar firmado o no, según su valor.

Lo que probablemente debería hacer, por cierto, es incluir limits.h y usar INT_MIN para el valor mínimo de un int , y LONG_MIN para el valor mínimo de un long . Tienen el valor correcto y el tipo esperado ( INT_MIN es un int , LONG_MIN es un long ). Si necesita un tipo exacto de 32 bits, entonces (suponiendo que su implementación sea un complemento de 2):

  • para el código que no tiene que ser portátil, puede usar el tipo que prefiera que tenga el tamaño correcto y afirmar que está en el lado seguro.
  • para el código que tiene que ser portátil, busque una versión del encabezado C99 stdint.h que funcione en su comstackdor C89 y use int32_t e INT32_MIN partir de eso.
  • si todo lo demás falla, escriba stdint.h usted mismo, y use la expresión en la respuesta de WiSaGaN. Tiene tipo int si int tiene al menos 32 bits, de lo contrario, es long .

Reemplazar

 #define INT32_MIN (-2147483648L) 

con

 #define INT32_MIN (-2147483647 - 1) 

-2147483648 es interpretado por el comstackdor como la negación de 2147483648 , que causa el desbordamiento en un int . Por lo tanto, debe escribir (-2147483647 - 1) lugar.
Esto es todo estándar C89 sin embargo. Vea la respuesta de Steve Jessop para C99 .
También es long generalmente 32 bits en máquinas de 32 bits y 64 bits en máquinas de 64 bits. int aquí hace las cosas.