(-2147483648> 0) renvoie vrai en C ++?

241

-2147483648 est le plus petit entier pour le type entier avec 32 bits, mais il semble qu'il débordera dans la if(...)phrase:

if (-2147483648 > 0)
    std::cout << "true";
else
    std::cout << "false";

Cela s'imprimera truedans mes tests. Cependant, si nous convertissons -2147483648 en entier, le résultat sera différent:

if (int(-2147483648) > 0)
    std::cout << "true";
else
    std::cout << "false";

Cela s'imprimera false.

Je suis confus. Quelqu'un peut-il donner une explication à ce sujet?


Mise à jour 02-05-2012:

Merci pour vos commentaires, dans mon compilateur, la taille de int est de 4 octets. J'utilise VC pour des tests simples. J'ai changé la description dans ma question.

C'est beaucoup de très bonnes réponses dans ce post, AndreyT a donné une explication très détaillée sur la façon dont le compilateur se comportera sur une telle entrée, et comment cet entier minimum a été implémenté. qPCR4vir , d'autre part, a donné quelques "curiosités" et comment les nombres entiers sont représentés. Tellement impressionnant!

benyl
la source
48
"nous savons tous que -2147483648 est le plus petit nombre entier" Cela dépend de la taille de l'entier.
orlp
14
"nous savons tous que -2147483648 est le plus petit nombre entier" - je pensais qu'il n'y avait pas de plus petit entier, car il y en a infiniment beaucoup ... Peu importe.
@Inisheer Avec 4 entiers Byte vous pouvez avoir un INT_MINde -9223372036854775808, siCHAR_BIT est 16. Et même avec CHAR_BIT == 8et sizeof(int== 4) `vous pouvez obtenir -9223372036854775807parce que C ne nécessite pas de nombres à 2 compléments.
12431234123412341234123

Réponses:

391

-2147483648n'est pas un "nombre". Le langage C ++ ne prend pas en charge les valeurs littérales négatives.

-2147483648 est en fait une expression: une valeur littérale positive 2147483648 avec un -opérateur unaire devant elle. La valeur 2147483648est apparemment trop grande pour le côté positif de la intplage sur votre plate-forme. Si le type long intavait une plus grande plage sur votre plateforme, le compilateur devrait automatiquement supposer qu'il 2147483648a le long inttype. (En C ++ 11, le compilateur devrait également prendre en compte le long long inttype.) Cela obligerait le compilateur à évaluer -2147483648dans le domaine de type plus grand et le résultat serait négatif, comme on pourrait s'y attendre.

Cependant, dans votre cas, la plage de long intest la même que la plage de int, et en général, il n'y a pas de type entier avec une plage plus grande que intsur votre plate-forme. Cela signifie formellement que la constante positive2147483648 déborde de tous les types d'entiers signés disponibles, ce qui signifie que le comportement de votre programme n'est pas défini. (Il est un peu étrange que la spécification du langage opte pour un comportement non défini dans de tels cas, au lieu d'exiger un message de diagnostic, mais c'est ainsi.)

Dans la pratique, la prise en compte du fait que le comportement n'est pas défini 2147483648peut être interprétée comme une valeur négative dépendante de l'implémentation qui devient positive après avoir été -appliquée à l'unary. Alternativement, certaines implémentations peuvent décider d'essayer d'utiliser des types non signés pour représenter la valeur (par exemple, en C89 / 90, les compilateurs devaient utiliser unsigned long int, mais pas en C99 ou C ++). Les implémentations sont autorisées à faire n'importe quoi, car le comportement n'est pas défini de toute façon.

En remarque, c'est la raison pour laquelle les constantes comme INT_MINsont généralement définies comme

#define INT_MIN (-2147483647 - 1)

au lieu de ce qui semble plus simple

#define INT_MIN -2147483648

Ce dernier ne fonctionnerait pas comme prévu.

Fourmi
la source
78
Ceci est également la raison pour laquelle cela est fait: #define INT_MIN (-2147483647 - 1).
orlp
5
@ RichardJ.RossIII - avec clang, vous obtenez probablement un littéral de type 64 bits, car il était trop gros pour tenir dans un int. L'implémentation d'OP peut ne pas avoir un type 64 bits.
Carl Norum
1
@ RichardJ.RossIII: Je crois que ce comportement est défini par l'implémentation / non défini.
Oliver Charlesworth
3
Je n'ai jamais pensé qu'un "nombre négatif" ne soit pas analysé comme tel. Je ne vois aucune raison. J'espère que -1.0c'est analysé comme une valeur double négative, n'est-ce pas?
leemes
6
@ qPCR4vir: Non. Comme je l'ai écrit dans mon commentaire à votre réponse, ni le C ni le C ++ modernes n'autorisent l'utilisation de types non signés dans ce cas (avec une constante décimale sans suffixe ). Seule la première norme C (C89 / 90) autorisée unsigned long intdans ce contexte, mais en C99 cette autorisation a été supprimée. Les littéraux non suffixés en C et C ++ doivent avoir des types signés . Si vous voyez un type non signé ici quand un signé fonctionnerait, cela signifie que votre compilateur est cassé. Si vous voyez ici un type non signé alors qu'aucun type signé ne fonctionnerait, alors ce n'est qu'une manifestation spécifique d'un comportement non défini.
AnT
43

Le compilateur (VC2012) promeut les entiers "minimum" pouvant contenir les valeurs. Dans le premier cas, signed int(et long int) ne peut pas (avant l'application du signe), mais unsigned intpeut: 2147483648aunsigned int ???? type. Dans la seconde, vous forcez à intpartir du unsigned.

const bool i= (-2147483648 > 0) ;  //   --> true

avertissement C4146: opérateur unaire moins appliqué au type non signé , résultat toujours non signé

Voici des "curiosités" liées:

const bool b= (-2147483647      > 0) ; //  false
const bool i= (-2147483648      > 0) ; //  true : result still unsigned
const bool c= ( INT_MIN-1       > 0) ; //  true :'-' int constant overflow
const bool f= ( 2147483647      > 0) ; //  true
const bool g= ( 2147483648      > 0) ; //  true
const bool d= ( INT_MAX+1       > 0) ; //  false:'+' int constant overflow
const bool j= ( int(-2147483648)> 0) ; //  false : 
const bool h= ( int(2147483648) > 0) ; //  false
const bool m= (-2147483648L     > 0) ; //  true 
const bool o= (-2147483648LL    > 0) ; //  false

Norme C ++ 11 :

2.14.2 Littéraux entiers [lex.icon]

Un littéral entier est une séquence de chiffres sans période ni partie exposante. Un littéral entier peut avoir un préfixe qui spécifie sa base et un suffixe qui spécifie son type.

Le type d'un littéral entier est le premier de la liste correspondante dans laquelle sa valeur peut être représentée.

entrez la description de l'image ici

Si un littéral entier ne peut être représenté par aucun type dans sa liste et qu'un type entier étendu (3.9.1) peut représenter sa valeur, il peut avoir ce type entier étendu. Si tous les types de la liste du littéral sont signés, le type entier étendu doit être signé. Si tous les types de la liste pour le littéral ne sont pas signés, le type entier étendu doit être non signé. Si la liste contient à la fois des types signés et non signés, le type entier étendu peut être signé ou non signé. Un programme est mal formé si l'une de ses unités de traduction contient un littéral entier qui ne peut être représenté par aucun des types autorisés.

Et ce sont les règles de promotion des entiers dans la norme.

4.5 Promotions intégrales [conv.prom]

A prvalue d'un type entier autre que bool, char16_t, char32_t, ou wchar_tdont le rang entier conversion (4,13) est inférieur au rang d'int peut être converti en un prvalue de type intsi intpeut représenter toutes les valeurs du type de source; sinon, la valeur source peut être convertie en valeur de type unsigned int.

qPCR4vir
la source
3
@ qPCR4vir: Dans C89 / 90 les compilateurs étaient supposés types d'utilisation int, long int, unsigned long intpour représenter des constantes décimales unsuffixed. C'était le seul langage qui permettait d'utiliser des types non signés pour des constantes décimales non suffixées. En C ++ 98, c'était intou long int. Aucun type non signé autorisé. Ni C (à partir de C99) ni C ++ ne permettent au compilateur d'utiliser des types non signés dans ce contexte. Votre compilateur est, bien sûr, libre d'utiliser des types non signés si aucun de ceux signés ne fonctionne, mais ce n'est encore qu'une manifestation spécifique d'un comportement non défini.
AnT
@AndreyT. Génial! Bien sûr, votre droit. VC2012 est-il cassé?
qPCR4vir
@ qPCR4vir: AFAIK, VC2012 n'est pas encore un compilateur C ++ 11 (n'est-ce pas?), ce qui signifie qu'il doit utiliser soit intou long intreprésenter 2147483648. En outre, AFAIK, dans VC2012 à la fois intet long intsont de type 32 bits. Cela signifie que dans VC2012, le littéral 2147483648doit conduire à un comportement non défini . Lorsque le comportement n'est pas défini, le compilateur est autorisé à faire n'importe quoi. Cela signifierait que VC2012 n'est pas cassé. Il a simplement émis un message de diagnostic trompeur. Au lieu de vous dire que le comportement est indéfini, il a décidé d'utiliser un type non signé.
AnT
@AndreyT: Êtes-vous en train de dire que les compilateurs sont libres d'émettre des démons nasaux si le code source contient un littéral décimal sans suffixe qui dépasse la valeur maximale d'un signé longet n'est pas obligé d'émettre un diagnostic? Cela semblerait cassé.
supercat
Même "avertissement C4146" dans VS2008 et "cette constante décimale n'est pas signée uniquement dans ISO C90" dans G ++
spyder
6

En bref, 2147483648déborde -2147483648et (-(-2147483648) > 0)est true.

Voilà à quoi 2147483648ressemble en binaire.

De plus, dans le cas de calculs binaires signés, le bit le plus significatif ("MSB") est le bit de signe. Cette question peut aider à expliquer pourquoi.

drzymala
la source
4

Parce que -2147483648c'est en fait 2147483648avec la négation ( -) qui lui est appliquée, le nombre n'est pas celui que vous attendez. C'est en fait l'équivalent de ce pseudocode:operator -(2147483648)

Maintenant, en supposant que votre compilateur est sizeof(int)égal à 4et CHAR_BITest défini comme 8, cela ferait 2147483648déborder la valeur maximale signée d'un entier ( 2147483647). Alors, quel est le maximum plus un? Permet de travailler avec un entier de complément de 4 bits et 2 s.

Attendre! 8 déborde l'entier! Qu'est-ce qu'on fait? Utilisez sa représentation non signée 1000et interprétez les bits comme un entier signé. Cette représentation nous laisse avec l' -8application de la négation du complément 2s qui en résulte 8, qui, comme nous le savons tous, est supérieure à 0.

C'est pourquoi <limits.h>(et <climits>) se définissent généralement INT_MINcomme ((-2147483647) - 1)- de sorte que l'entier signé maximum ( 0x7FFFFFFF) soit nié ( 0x80000001), puis décrémenté ( 0x80000000).

Cole Johnson
la source
Pour un nombre de 4 bits, la négation du complément à deux de -8est toujours -8.
Ben Voigt
Sauf que -8 est interprété comme 0-8, pas négatif 8. Et 8 déborde un 4 bits signé int
Cole Johnson
Considérez -(8)lequel en C ++ est le même que -8- c'est la négation appliquée à un littéral, pas un littéral négatif. Le littéral est 8, qui ne tient pas dans un entier signé de 4 bits, il doit donc être non signé. Le motif est 1000. Jusqu'à présent, votre réponse est correcte. La négation du complément à deux de 1000en 4 bits est 1000, peu importe si elle est signée ou non signée. Votre réponse, dit "interpréter les bits comme un entier signé" qui fait la valeur -8après la négation du complément à deux, tout comme c'était avant la négation.
Ben Voigt
Bien sûr, en "C ++ 4 bits", il n'y a pas "interpréter les bits comme une étape d'entier signé". Le littéral devient le plus petit type qui puisse l'exprimer, qui est un entier 4 bits non signé . La valeur du littéral est 8. La négation est appliquée (modulo 16), ce qui donne une réponse finale de 8. L'encodage est toujours 1000 mais la valeur est différente car un type non signé a été choisi.
Ben Voigt