62 votes

Pourquoi - (- 2147483648) = - 2147483648 sur une machine 32 bits?

Je pense que la question est explicite, je suppose que cela a probablement quelque chose à voir avec le débordement, mais je ne comprends toujours pas. Que se passe-t-il, au niveau des bits, sous le capot?

Pourquoi -(-2147483648) = -2147483648 (au moins lors de la compilation en C)?

74voto

Grzegorz Szpetkowski Points 10225

En niant un (unsuffixed) constante entière:

L'expression -(-2147483648) est parfaitement défini en C, mais il peut ne pas être évident pourquoi il agit de cette façon.

Lorsque vous écrivez -2147483648, il est formé en tant que unaire moins opérateur appliqué à la constante entière. Si 2147483648 ne peut pas être exprimée en int, puis il s est représenté comme long ou long long* (selon ce qui convient en premier), où le dernier type est garanti par la Norme pour couvrir cette valeur.

Pour confirmer que vous pourriez examiner par:

printf("%zu\n", sizeof(-2147483648));

ce qui donne 8 sur ma machine.

L'étape suivante consiste à appliquer une seconde - opérateur, auquel cas la valeur finale est - 2147483648L (en supposant qu'il a finalement été représentés en long). Si vous essayez de l'attribuer à l' int objet, comme suit:

int n = -(-2147483648);

ensuite, le comportement réel est mise en œuvre définies. Se référant à la Norme:

C11 §6.3.1.3/3 entiers signés et non Signés

Sinon, le nouveau type est signé et que la valeur ne peut pas être représenté dans celle-ci; soit le résultat est la mise en œuvre définis ou une la mise en œuvre définies par le signal est déclenché.

La façon la plus commune est de simplement couper l'bits supérieurs. Par exemple, GCC documents comme:

Pour la conversion d'un type de largeur de N, la valeur est réduite modulo 2^N la portée de ce type; aucun signal n'est soulevée.

Sur le plan conceptuel, la conversion de type de largeur 32 peut être illustré par le bit à bit ET de l'exploitation:

value & (2^32 - 1) // preserve 32 least significant bits

En conformité avec en complément à deux arithmétique, la valeur de n est formé avec tous les zéros et de l'ESM (signe) ensemble de bits, qui représente la valeur de -2^31, c'est - -2147483648.

En niant l' int objet:

Si vous essayez de nier int objet, qui contient la valeur de -2147483648, et en supposant que complément à deux de la machine, le programme présentent un comportement indéfini:

n = -n; // UB if n == INT_MIN and INT_MAX == 2147483647

C11 §6.5/5 Expressions

Si un état exceptionnel se produit lors de l'évaluation d'une l'expression (qui est, si le résultat n'est pas définie mathématiquement ou pas dans la gamme des représentable valeurs pour son type), le comportement n'est pas défini.

Références supplémentaires:


*) Dans withdrawed C90 Standard, il n'y avait pas long long type et les règles étaient différentes. Plus précisément, la séquence de unsuffixed décimal a été int, long int, unsigned long int (C90 §6.1.3.2 les constantes entières).

†) Cela est dû à l' LLONG_MAX, qui doit être d'au moins +9223372036854775807 (C11 §5.2.4.2.1/1).

16voto

Antti Haapala Points 11542

Remarque: cette réponse ne s'applique pas en tant que tel sur le obsolète ISO C90 standard qui est encore utilisé par de nombreux compilateurs

Tout d'abord, sur le C99, C11, l'expression -(-2147483648) == -2147483648 est en fait faux:

int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);

imprime

0

Alors, comment il est possible que cela s'évalue à vrai? La machine est à l'aide de 32 bits en complément à deux nombres entiers. L' 2147483648 est un entier constant que tout ne rentre pas en 32 bits, donc ce sera soit long int ou long long int selon la est le premier où il se situe. Cette niée entraînera -2147483648 - et encore une fois, même si le nombre -2147483648 peut s'adapter à un entier de 32 bits, l'expression -2147483648 se compose d'un >32 bits nombre entier positif précédé unaire -!

Vous pouvez essayer le programme suivant:

#include <stdio.h>

int main() {
    printf("%zu\n", sizeof(2147483647));
    printf("%zu\n", sizeof(2147483648));
    printf("%zu\n", sizeof(-2147483648));
}

La sortie sur la machine, le plus probablement, de 4, 8 et 8.

Maintenant, -2147483648 annulés seront à nouveau résultat en +214783648, ce qui est toujours de type long int ou long long int, et tout va bien.

En C99, C11, la constante entière expression -(-2147483648) est bien définie sur l'ensemble conforme implémentations.


Maintenant, lorsque cette valeur est affectée à une variable de type int, avec 32 bits et en complément à deux de la représentation, la valeur n'est pas représentable dans - les valeurs sur 32 bits en complément de 2 seraient de l'ordre de -2147483648 à 2147483647.

La norme C11 6.3.1.3p3 dit ce qui suit entier conversions:

  • [Lorsque] le nouveau type est signé et que la valeur ne peut pas être représenté, soit le résultat est la mise en œuvre défini ou d'une mise en œuvre définies par le signal est déclenché.

Qui est, le C standard n'est pas réellement définir ce que la valeur serait dans ce cas, ou de ne pas exclure la possibilité que l'exécution du programme s'arrête à cause d'un signal d'être posée, mais laisse à la mise en œuvre (c'est à dire des compilateurs) pour décider de la façon de le gérer (C11 3.4.1):

la mise en œuvre définies par le comportement

un comportement non spécifié où chaque mise en œuvre des documents de la façon dont le choix est fait

et (3.19.1):

mise en valeur définie

valeur quelconque où chaque mise en œuvre des documents de la façon dont le choix est fait


Dans votre cas, la mise en œuvre définies par le comportement, c'est que la valeur est le 32 plus bas bits de [*]. En raison de la 2 en complément, le (long) de long int valeur 0x80000000 a le bit 31 jeu et tous les autres bits effacé. En 32 bits en complément à deux entiers le bit 31 est le bit de signe - signifie que le nombre est négatif; tous les bits à zéro signifie que la valeur est le minimum de nombre représentable, c'est à dire INT_MIN.


[*] GCC documents de sa mise en œuvre définies par le comportement dans ce cas comme suit:

Le résultat, ou le signal soulevées par, la conversion d'un nombre entier à un nombre entier signé de type lorsque la valeur ne peut pas être représenté dans un objet de ce type (C90 6.2.1.2, C99 et C11 6.3.1.3).

Pour la conversion d'un type de largeur N, la valeur est réduite modulo 2^N la portée de ce type; aucun signal n'est soulevée.

5voto

John Bollinger Points 16563

Ce n'est pas une question C, pour une implémentation C doté de 32 bits en complément à deux de la représentation de type int, l'effet de l'application de la unaire de négation de l'opérateur à un int ayant la valeur -2147483648 est pas défini. Qui est, le langage C spécifiquement renient désignant le résultat de l'évaluation d'une telle opération.

Envisager de manière plus générale, cependant, la façon dont le unaire - opérateur est défini en complément à deux arithmétique: l'inverse d'un nombre positif x est formé en inversant tous les bits de la représentation binaire et l'ajout d' 1. Cette même définition sert bien pour tout nombre négatif qui a au moins un bit d'autre que son bit de signe ensemble.

Les petits problèmes se posent, cependant, pour les deux nombres qui n'ont aucune valeur en bits: 0, ce qui n'a pas de bits à tous, et dont le nombre n'a que son bit de signe set (-2147483648 à 32 bits de la représentation). Lorsque vous retournez tous les bits de l'un de ces, vous vous retrouvez avec tous les bits définis. Par conséquent, lorsque vous ajoutez ultérieurement 1, le résultat des dépassements de la valeur des bits. Si vous imaginez effectuer l'addition comme si le nombre ont été signés, le traitement, le bit de signe est une valeur à peu, puis vous obtenez des

    -2147483648 (decimal representation)
-->  0x80000000 (convert to hex)
-->  0x7fffffff (flip bits)
-->  0x80000000 (add one)
--> -2147483648 (convert to decimal)

Similaire s'applique à l'inversion de zéro, mais dans ce cas, le dépassement de capacité lors de l'ajout de 1 débordements de l'ancienne bit de signe, trop. Si le dépassement est ignoré, le 32 bits sont toutes à zéro, d'où -0 == 0.

0voto

plugwash Points 795

Cela dépend de la version de C, les détails de la mise en œuvre et si nous parlons des variables ou des valeurs littérales.

La première chose à comprendre est qu'il n'y a pas négatif les littéraux entiers en C "-2147483648" est unaire moins suivie par un littéral entier positif.

Supposons que nous sommes en cours d'exécution sur un typique de la plate-forme 32 bits où int et long sont à la fois 32 bits et long long est de 64 bits et d'envisager l'expression.

(-(-2147483648) == -2147483648 )

Le compilateur a besoin de trouver un type qui peut contenir 2147483648, sur un comforming compilateur C99, il va utiliser le type "long long" mais un C90 compilateur peut utiliser le type "unsigned long".

Si le compilateur utilise le type long long puis plus rien, des débordements et la comparaison est fausse. Si le compilateur utilise unsigned long alors non signé enveloppante règles entrent en jeu et la comparaison est vraie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X