tag name keywords etiquetas ejemplos description content c signed numeric-limits numeric-conversion

c - name - meta tags seo



¿Por qué es 0<-0x80000000? (6)

C tiene una regla de que el literal entero puede estar signed o unsigned depende de si cabe en signed o unsigned (promoción de enteros). En una máquina de 32 bits, el literal 0x80000000 estará unsigned . El complemento de 2 de -0x80000000 es 0x80000000 en una máquina de 32 bits. Por lo tanto, la comparación bal < INT32_MIN es entre con signed y unsigned y antes de la comparación según la regla C unsigned int se convertirá en long long .

C11: 6.3.1.8/1:

[...] De lo contrario, si el tipo de operando con tipo entero con signo puede representar todos los valores del tipo de operando con tipo entero sin signo, entonces el operando con tipo entero sin signo se convierte al tipo del operando con tipo entero con signo.

Por lo tanto, bal < INT32_MIN es siempre true .

Tengo debajo un programa simple:

#include <stdio.h> #define INT32_MIN (-0x80000000) int main(void) { long long bal = 0; if(bal < INT32_MIN ) { printf("Failed!!!"); } else { printf("Success!!!"); } return 0; }

La condición if(bal < INT32_MIN ) siempre es verdadera. ¿Como es posible?

Funciona bien si cambio la macro a:

#define INT32_MIN (-2147483648L)

¿Alguien puede señalar el problema?


Este entero literal 0x80000000 tiene el tipo unsigned int .

Según el estándar C (6.4.4.1 constantes enteras)

5 El tipo de una constante entera es el primero de la lista correspondiente en la que se puede representar su valor.

Y esta constante entera se puede representar por el tipo de unsigned int .

Entonces esta expresión

-0x80000000 tiene el mismo tipo unsigned int . Además, tiene el mismo valor 0x80000000 en la representación del complemento de dos que calcula de la siguiente manera

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

Esto tiene un efecto secundario si escribir por ejemplo

int x = INT_MIN; x = abs( x );

El resultado será nuevamente INT_MIN .

Así en esta condición

bal < INT32_MIN

se compara 0 con el valor sin signo 0x80000000 convertido a tipo long long int según las reglas de las conversiones aritméticas habituales.

Es evidente que 0 es menor que 0x80000000 .


Esto es bastante sutil.

Cada literal entero en su programa tiene un tipo. El tipo que tiene está regulado por una tabla en 6.4.4.1:

Suffix Decimal Constant Octal or Hexadecimal Constant none int int long int unsigned int long long int long int unsigned long int long long int unsigned long long int

Si un número literal no cabe dentro del tipo int predeterminado, intentará el siguiente tipo más grande como se indica en la tabla anterior. Entonces, para los literales enteros decimales regulares es así:

  • Prueba int
  • Si no puede caber, intente long
  • Si no cabe, intente long long .

Sin embargo, los literales hexadecimales se comportan de manera diferente. Si el literal no puede caber dentro de un tipo con signo como int , primero intentará unsigned int antes de pasar a probar tipos más grandes. Vea la diferencia en la tabla anterior.

Entonces, en un sistema de 32 bits, su literal 0x80000000 es de tipo unsigned int .

Esto significa que puede aplicar el operador unario en el literal sin invocar un comportamiento definido por la implementación, como lo haría al desbordar un entero con signo. En cambio, obtendrá el valor 0x80000000 , un valor positivo.

bal < INT32_MIN invoca las conversiones aritméticas habituales y el resultado de la expresión 0x80000000 se promueve de unsigned int a long long . El valor 0x80000000 se conserva y 0 es menor que 0x80000000, de ahí el resultado.

Cuando reemplaza el literal con 2147483648L , usa la notación decimal y, por lo tanto, el compilador no elige unsigned int , sino que intenta ajustarlo dentro de un long . También el sufijo L dice que quieres un long si es posible . El sufijo L en realidad tiene reglas similares si continúa leyendo la tabla mencionada en 6.4.4.1: si el número no cabe dentro del long solicitado, lo que no sucede en el caso de 32 bits, el compilador le dará un long long donde encajará bien.


La constante numérica 0x80000000 es de tipo unsigned int . Si tomamos -0x80000000 y hacemos 2s de matemática complementaria, obtenemos esto:

~0x80000000 = 0x7FFFFFFF 0x7FFFFFFF + 1 = 0x80000000

Entonces -0x80000000 == 0x80000000 . Y la comparación (0 < 0x80000000) (ya que 0x80000000 no está firmado) es verdadera.


Un punto de confusión ocurre al pensar que - es parte de la constante numérica.

En el siguiente código 0x80000000 es la constante numérica. Su tipo se determina solo en eso. El - se aplica después y no cambia el tipo .

#define INT32_MIN (-0x80000000) long long bal = 0; if (bal < INT32_MIN )

Las constantes numéricas sin adornos sin procesar son positivas.

Si es decimal, entonces el tipo asignado es el primer tipo que lo contendrá: int , long , long long .

Si la constante es octal o hexadecimal, obtiene el primer tipo que la contiene: int , unsigned , long , unsigned long , long long , unsigned long long .

0x80000000 , en el sistema de OP obtiene el tipo de unsigned long unsigned o unsigned long . De cualquier manera, es un tipo sin signo.

-0x80000000 también es un valor distinto de cero y es un tipo sin signo, es mayor que 0. Cuando el código compara eso con un long long , los valores no cambian en los 2 lados de la comparación, por lo que 0 < INT32_MIN es verdadero.

Una definición alternativa evita este comportamiento curioso.

#define INT32_MIN (-2147483647 - 1)

Caminemos en tierra de fantasía por un tiempo donde int y unsigned son de 48 bits.

Entonces 0x80000000 encaja en int y también lo es el tipo int . -0x80000000 es un número negativo y el resultado de la impresión es diferente.

[Volver a la palabra real]

Dado que 0x80000000 cabe en algún tipo sin signo antes de un tipo con signo, ya que es más grande que some_signed_MAX pero dentro de some_unsigned_MAX , es un tipo sin signo.


0x80000000 es un literal unsigned con valor 2147483648.

Aplicar el signo menos unario en esto todavía le da un tipo sin signo con un valor distinto de cero. (De hecho, para un valor distinto de cero x , el valor con el que termina es UINT_MAX - x + 1 ).