Eu tenho abaixo um programa simples:
#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;
}
A condição if(bal < INT32_MIN )
é sempre verdadeira. Como isso é possível?
Funciona bem se eu alterar a macro para:
#define INT32_MIN (-2147483648L)
Alguém pode apontar o problema?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
fonte
fonte
CHAR_BIT * sizeof(int)
?-0x80000000
, mas falsa para-0x80000000L
,-2147483648
e-2147483648L
(gcc 4.1.2), então a questão é: porque é que o int literal-0x80000000
diferente do literal int-2147483648
?<limits.h>
defineINT_MIN
como(-2147483647 - 1)
, agora você sabe o porquê.Respostas:
Isso é bastante sutil.
Todo literal inteiro no seu programa tem um tipo. Que tipo possui é regulado por uma tabela no 6.4.4.1:
Se um número literal não puder caber dentro do
int
tipo padrão , ele tentará o próximo tipo maior, conforme indicado na tabela acima. Portanto, para literais inteiros decimais regulares, é assim:int
long
long long
.Literais hexadecimais se comportam de maneira diferente! Se o literal não puder caber em um tipo assinado
int
, ele tentará primeirounsigned int
antes de tentar tipos maiores. Veja a diferença na tabela acima.Portanto, em um sistema de 32 bits, seu literal
0x80000000
é do tipounsigned int
.Isso significa que você pode aplicar o
-
operador unário no literal sem chamar o comportamento definido pela implementação, como faria ao estourar um número inteiro assinado. Em vez disso, você receberá o valor0x80000000
, um valor positivo.bal < INT32_MIN
chama as conversões aritméticas usuais e o resultado da expressão0x80000000
é promovido deunsigned int
paralong long
. O valor0x80000000
é preservado e 0 é menor que 0x80000000, daí o resultado.Quando você substitui o literal,
2147483648L
usa a notação decimal e, portanto, o compilador não escolheunsigned int
, mas tenta ajustá-lo dentro de along
. Além disso, o sufixo L indica que você deseja um,long
se possível . O sufixo L realmente possui regras semelhantes se você continuar lendo a tabela mencionada no 6.4.4.1: se o número não couber dentro do solicitadolong
, o que não acontece no caso de 32 bits, o compilador lhe dará um locallong long
onde vai caber muito bem.fonte
long
sistema de 32 bits2147483648L
, não cabe em umlong
, então ele se tornalong long
, então o-
é aplicado - ou assim eu pensava.0x7FFFFFFF
. Tente você mesmo:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
quando escrito no código fonte é sempre um número positivo, masint
é claro que sua variável pode conter números binários brutos até o valor 0xFFFFFFFF.ìnt n = 0x80000000
força uma conversão do literal não assinado para um tipo assinado. O que acontecerá depende do seu compilador - é um comportamento definido pela implementação. Nesse caso, ele escolheu mostrar todo o literal noint
, substituindo o bit do sinal. Em outros sistemas, pode não ser possível representar o tipo e você invoca um comportamento indefinido - o programa pode falhar. Você obterá o mesmo comportamento, se o fizerint n=2147483648;
, não está relacionado à notação hexadecimal.-
é aplicado a números inteiros não assinados pode ser expandido um pouco. Eu sempre assumi (embora felizmente nunca confiei na suposição) que valores não assinados seriam "promovidos" a valores assinados, ou possivelmente que o resultado seria indefinido. (Honestamente, ele deve ser um erro de compilação, o que é que- 3u
isso quer dizer?)0x80000000
é umunsigned
literal com o valor 2147483648.A aplicação do menos unário nisso ainda fornece um tipo não assinado com um valor diferente de zero. (De fato, para um valor diferente de zero
x
, o valor com o qual você termina éUINT_MAX - x + 1
).fonte
Este literal inteiro
0x80000000
tem tipounsigned int
.De acordo com o padrão C (6.4.4.1 Constantes inteiras)
E essa constante inteira pode ser representada pelo tipo de
unsigned int
.Então essa expressão
-0x80000000
tem o mesmounsigned int
tipo. Além disso, tem o mesmo valor0x80000000
na representação do complemento dos dois que calcula da seguinte maneiraIsso tem um efeito colateral se escrever, por exemplo
O resultado será novamente
INT_MIN
.Assim, nesta condição
é comparado
0
com o valor não assinado0x80000000
convertido para o tipo long long int, de acordo com as regras das conversões aritméticas comuns.É evidente que 0 é menor que
0x80000000
.fonte
A constante numérica
0x80000000
é do tipounsigned int
. Se fizermos-0x80000000
e fizermos 2s como complemento de matemática, obtemos o seguinte:Então
-0x80000000 == 0x80000000
. E comparar(0 < 0x80000000)
(já que0x80000000
não está assinado) é verdadeiro.fonte
int
s de 32 bits . Embora essa seja uma escolha muito comum, em qualquer implementação específicaint
pode ser mais estreita ou mais ampla. É uma análise correta para esse caso, no entanto.-0x80000000
é uma aritmética sem sinal.~0x800000000
é um código diferente.-0x80000000
! De fato, o complemento de 2 é totalmente irrelevante para esta questão.Um ponto de confusão ocorre ao pensar que
-
é parte da constante numérica.No código abaixo
0x80000000
está a constante numérica. Seu tipo é determinado apenas nisso. O-
é aplicado posteriormente e não altera o tipo .As constantes numéricas não adornadas não processadas são positivas.
Se for decimal, então o tipo atribuído é o primeiro tipo que vai prendê-lo:
int
,long
,long long
.Se a constante é octal ou hexadecimal, torna-se o primeiro tipo que mantém:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, no sistema do OP obtém o tipo deunsigned
ouunsigned long
. De qualquer forma, é algum tipo não assinado.-0x80000000
também é um valor diferente de zero e, sendo um tipo não assinado, é maior que 0. Quando o código compara isso along long
, os valores não são alterados nos dois lados da comparação, assim0 < INT32_MIN
é verdade.Uma definição alternativa evita esse comportamento curioso
Vamos caminhar na terra da fantasia por um tempo, onde
int
eunsigned
são de 48 bits.Então
0x80000000
se encaixaint
e assim é o tipoint
.-0x80000000
é então um número negativo e o resultado da impressão é diferente.[Voltar à palavra real]
Como
0x80000000
se encaixa em algum tipo não assinado antes de um tipo assinado, pois é um pouco maior do quesome_signed_MAX
ainda dentrosome_unsigned_MAX
, é um tipo não assinado.fonte
C tem uma regra de que o literal inteiro pode ser
signed
ouunsigned
depende de se encaixarsigned
ouunsigned
(promoção de número inteiro). Em uma32
máquina de bits, o literal0x80000000
seráunsigned
. O complemento do 2-0x80000000
está0x80000000
em uma máquina de 32 bits. Portanto, a comparaçãobal < INT32_MIN
é entresigned
eunsigned
antes da comparação, conforme a regra Cunsigned int
será convertida emlong long
.C11: 6.3.1.8/1:
Portanto,
bal < INT32_MIN
é sempretrue
.fonte