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 )
es siempre cierta. ¿Como es posible?
Funciona bien si cambio la macro a:
#define INT32_MIN (-2147483648L)
¿Alguien puede señalar el problema?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
fuente
fuente
CHAR_BIT * sizeof(int)
?-0x80000000
, pero falsa para-0x80000000L
,-2147483648
y-2147483648L
(gcc 4.1.2), así que la pregunta es: ¿por qué es el int literal-0x80000000
diferente de lo literal int-2147483648
?<limits.h>
defineINT_MIN
como(-2147483647 - 1)
, ahora sabes por qué.Respuestas:
Esto es bastante sutil.
Cada literal entero en su programa tiene un tipo. Qué tipo tiene está regulado por una tabla en 6.4.4.1:
Si un número literal no cabe dentro del
int
tipo predeterminado , intentará el siguiente tipo más grande como se indica en la tabla anterior. Entonces, para los literales enteros decimales regulares es así:int
long
long long
.Sin embargo, los literales hexadecimales se comportan de manera diferente. Si el literal no cabe dentro de un tipo con signo como
int
, primero lo 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 tipounsigned 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 valor0x80000000
, un valor positivo.bal < INT32_MIN
invoca las conversiones aritméticas habituales y el resultado de la expresión0x80000000
se promueve deunsigned int
along long
. El valor0x80000000
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 eligeunsigned int
, sino que intenta ajustarlo dentro de along
. También el sufijo L dice que quieres unlong
si es posible . El sufijo L realidad tiene reglas similares si continúa para leer la tabla mencionada en 6.4.4.1: si el número no encaja dentro de la solicitadalong
, que no en el caso de 32 bits, el compilador le dará unalong long
donde Te quedará bien.fuente
long
del sistema2147483648L
, no caben en unalong
, lo que se conviertelong long
, entonces el-
se aplica - o eso creía yo.0x7FFFFFFF
. Pruébelo usted mismo:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
cuando se escribe en el código fuente siempre es un número positivo, pero suint
variable puede, por supuesto, contener números binarios en bruto hasta el valor 0xFFFFFFFF.ìnt n = 0x80000000
fuerza una conversión del literal sin signo a un tipo con signo. Lo que sucederá depende de su compilador: es un comportamiento definido por la implementación. En este caso, eligió mostrar el literal completo en elint
, sobrescribiendo el bit de signo. En otros sistemas, es posible que no sea posible representar el tipo e invocar un comportamiento indefinido; el programa puede fallar. Obtendrá el mismo comportamiento si lo hace,int n=2147483648;
no está relacionado con la notación hexadecimal en absoluto.-
se aplica unario a enteros sin signo podría ampliarse un poco. Siempre supuse (aunque afortunadamente nunca me basé en la suposición) que los valores sin signo se "promoverían" a valores con signo, o posiblemente que el resultado sería indefinido. (Honestamente, debería ser un error de compilación; ¿qué significa- 3u
incluso?)0x80000000
es ununsigned
literal 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 esUINT_MAX - x + 1
).fuente
Este entero literal
0x80000000
tiene tipounsigned int
.Según el estándar C (6.4.4.1 constantes enteras)
Y esta constante entera se puede representar por el tipo de
unsigned int
.Entonces esta expresión
-0x80000000
Tiene el mismounsigned int
tipo. Además, tiene el mismo valor0x80000000
en la representación del complemento de dos que calcula de la siguiente maneraEsto tiene un efecto secundario si escribir por ejemplo
El resultado será de nuevo
INT_MIN
.Así en esta condición
se compara
0
con el valor sin signo0x80000000
convertido a tipo long long int de acuerdo con las reglas de las conversiones aritméticas habituales.Es evidente que 0 es menor que
0x80000000
.fuente
La constante numérica
0x80000000
es de tipounsigned int
. Si tomamos-0x80000000
y hacemos matemáticas complementarias de 2s, obtenemos esto:Por lo tanto
-0x80000000 == 0x80000000
. Y comparar(0 < 0x80000000)
(ya0x80000000
que no está firmado) es cierto.fuente
int
s. Aunque esa es una opción muy común, en cualquier implementaciónint
puede ser más estrecha o más amplia. Sin embargo, es un análisis correcto para ese caso.-0x80000000
es aritmética sin signo.~0x800000000
Es un código diferente.-0x80000000
! De hecho, el complemento de 2 es irrelevante para esta pregunta por completo.Se produce un punto de confusión al pensar que
-
es parte de la constante numérica.En el siguiente código
0x80000000
está la constante numérica. Su tipo se determina solo en eso. El-
se aplica después y no cambia el tipo .Las constantes numéricas sin adornos sin procesar son positivas.
Si es decimal, entonces el tipo asignado es el primer tipo que lo sostienen:
int
,long
,long long
.Si la constante es octal o hexadecimal, se pone el primer tipo que lo sostiene:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, en el sistema de OP obtiene el tipo deunsigned
ounsigned 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 along long
, los valores no cambian en los 2 lados de la comparación, por lo que0 < INT32_MIN
es cierto.Una definición alternativa evita este comportamiento curioso.
Caminemos en tierra de fantasía por un tiempo donde
int
yunsigned
somos 48 bits.Luego
0x80000000
encajaint
y también lo es el tipoint
.-0x80000000
es entonces 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 quesome_signed_MAX
todavía dentrosome_unsigned_MAX
, es un tipo sin signo.fuente
C tiene una regla de que el literal entero puede ser
signed
ounsigned
depende de si encajasigned
ounsigned
(promoción de enteros). En una32
máquina de un bit, el literal0x80000000
seráunsigned
. El complemento de 2-0x80000000
está0x80000000
en una máquina de 32 bits. Por lo tanto, la comparaciónbal < INT32_MIN
es entresigned
yunsigned
antes de la comparación según la regla Cunsigned int
se convertirálong long
.C11: 6.3.1.8/1:
Por lo tanto,
bal < INT32_MIN
es siempretrue
.fuente