Pourquoi la négation de l’entier minimum possible cède-t-elle?

J’ai donc écrit une petite expérience testant les débordements et débordements, en utilisant c et une machine 64 bits. Pour le type int, les valeurs min / max sont les suivantes:

int tmax = 2147483647; int tmin = -2147483648; 

Je sais comprendre comment deux-complément fonctionne, ce n’est pas ma question.

Alors j’ai pensé, bien que se passe-t-il si je fais quelque chose de négatif tmin? C’est:

  int tmin = -2147483648; int negativeTmin = -tmin; 

Il finit toujours par être tmin. (C’est-à-dire que negativeTmin sera -2147483648)

Ma question est pourquoi est-ce? Puisque 2 147 483 648 positifs ne peuvent pas être représentés par int, je comprends pourquoi ce n’est pas bien sûr, mais il semble étrange que cela ne change pas du tout, car c’est le seul entier non nul qui ne change pas quand – s’applique à cela. Je ne dis pas que j’ai une meilleure idée de ce que cela devrait être, je suis juste curieux de savoir pourquoi -tmin == tmin. Cela a-t-il quelque chose à voir avec les opérations au niveau des bits, ou comment la soustraction est effectuée dans un ordinateur, ou est-ce par défaut de le faire parce que ce que j’essaie de faire est indéfini ou quelque chose d’autre?

Mon code:

 #include  int main() { int tmax = 2147483647; printf("tmax Before: %d\n", tmax); tmax++; printf("tmax After: %d\n\n", tmax); int tmin = -2147483648; printf("tmin Before: %d\n", tmin); tmin--; printf("tmin After: %d\n\n", tmin); int tmin2 = -2147483648; int negativeTmin = -tmin2; printf("negative tmin: %d\n\n", negativeTmin); return 0; } 

Sortie:

tmax Avant: 2147483647 tmax Après: -2147483648

tmin Avant: -2147483648 tmin Après: 2147483647

tmin négatif: -2147483648

Comme d’autres personnes l’ont signalé ici, techniquement, ce que vous faites conduit à un comportement indéfini, car un dépassement ou un dépassement insuffisant d’un entier signé en C entraîne un comportement indéfini.

D’un autre côté, sur la plupart des systèmes Intel, un dépassement ou un dépassement entier entoure simplement la valeur entière et définit un indicateur de processeur afin que les instructions futures puissent détecter le dépassement. Sur ces systèmes, il est raisonnable de demander: pourquoi obtenez-vous T min lorsque vous calculez -T min ?

Dans le système du complément à deux signé, il est bon de noter que l’expression -x est équivalente à ~x + 1 . Alors imaginons que vous avez T min , qui ressemble à ceci:

 10000000 00000000 00000000 00000000 

Si vous calculez ~ T min , vous obtenez

 01111111 11111111 11111111 11111111 

Cela arrive à être T max . Si vous en ajoutez un à celui-ci, vous obtenez un report d’ondes massif se propageant jusqu’à la fin, ce qui donne

 10000000 00000000 00000000 00000000 

c’est ce avec quoi nous avons commencé. C’est pourquoi vous voyez probablement le retour de T min .

Une autre façon de voir ceci: vous savez que T min pour un entier signé de 32 bits est -2 31 . La valeur de -Tmin doit être une valeur telle que Tmin + -Tmin = 0 (mod 2 32 ). Alors, quelle valeur dans la plage [-2 31 , 2 31 – 1] a-t-elle cette propriété? C’est -2 31 , c’est pourquoi T min = -T min .

La meilleure réponse à votre question est donc probablement “techniquement ce que vous faites est un comportement indéfini, mais sur un système Intel raisonnable et un compilateur qui n’est pas configuré pour faire des optimisations agressives, il s’agit de la mécanique de la signature 32 – l’arithmétique des bits entiers fonctionne et comment la négation est définie. ”

Votre code int tmin2 = -2147483648; int negativeTmin = -tmin2 int tmin2 = -2147483648; int negativeTmin = -tmin2 introduit un comportement indéfini en raison d’un débordement d’entier, ainsi il peut produire n’importe quel résultat. Penser à toutes les règles qui expliquent pourquoi cela se produit et s’il s’agit d’un complément de deux n’a aucun sens et est en réalité faux.

Un dépassement de capacité intégral est l’ exemple d’un comportement non défini, comme il est mentionné à titre d’exemple dans la définition standard du “comportement non défini” ( 3.4.3 – comportement non défini ):

1 comportement non défini, lors de l’utilisation d’un programme de construction non portable ou erroné, ou de données erronées, pour lequel la présente Norme internationale n’impose aucune exigence

2 NOTE Le comportement indéfini possible va d’ignorer complètement la situation avec des résultats imprévisibles, de se comporter pendant la traduction ou l’exécution du programme de manière documentée, caractéristique de l’environnement (avec ou sans émission d’un message de diagnostic), jusqu’à la fin de la traduction ou de l’exécution (avec l’émission d’un message de diagnostic).

3 EXEMPLE Un exemple de comportement non défini est le comportement en cas de dépassement d’entier.

-X est un complément à 2 de X
Et c’est ce que le matériel fait pour la négation https://c9x.me/x86/html/file_module_x86_id_216.html

INT_MIN = -2147483648 = 0x80000000
Complément de 2 de -2147483648 = 0x80000000

Vous pouvez calculer le complément de 2 sous forme de bits d’inversion et append 1
voir https://en.wikipedia.org/wiki/Two%27s_complement
Inverser les bits de 0x80000000 donne 0x7fffffff.
0x7fffffff + 1 = 0x80000000 = -2147483648

(gdb) p / x (int) (- 2147483648)
14 $ = 0x80000000
(gdb) p / x (int) – (- 2147483648)
15 $ = 0x80000000
(gdb) p / x ~ (0x80000000)
$ 16 = 0x7fffffff
(gdb) p / x ~ (0x80000000) + 1
17 $ = 0x80000000

En fait, je crois que je viens de réaliser la réponse; Comme nous le soaps, 2147483648 ne peut pas être représenté par int, il en est de même pour -2147483648. Ainsi, lorsque vous faites bien – (- 2147483648), vous obtenez bien 2147483648, mais vous ne pouvez pas le représenter comme décrit ci-dessus, qui devient -2147483648. D’où pourquoi -tmin == tmin.