Le débordement d'entier non signé est bien défini par les normes C et C ++. Par exemple, la norme C99 ( §6.2.5/9
) indique
Un calcul impliquant des opérandes non signés ne peut jamais déborder, car un résultat qui ne peut pas être représenté par le type entier non signé résultant est réduit modulo le nombre qui est supérieur à la plus grande valeur qui peut être représentée par le type résultant.
Cependant, les deux normes indiquent que le dépassement d'entier signé est un comportement non défini. Encore une fois, à partir de la norme C99 ( §3.4.3/1
)
Un exemple de comportement indéfini est le comportement sur le débordement d'entier
Y a-t-il une raison historique ou (encore mieux!) Une raison technique à cet écart?
c++
c
undefined-behavior
integer-overflow
Anthony Vallée-Dubois
la source
la source
if (a + b < a)
). Le débordement lors de la multiplication est difficile pour les types signés et non signés.MAX_INT+1 == -0
, alors que sur un complément à deux ce seraitINT_MIN
Réponses:
La raison historique est que la plupart des implémentations C (compilateurs) ont simplement utilisé le comportement de débordement le plus facile à implémenter avec la représentation entière utilisée. Les implémentations C utilisaient généralement la même représentation que celle utilisée par le CPU - donc le comportement de débordement découle de la représentation entière utilisée par le CPU.
En pratique, seules les représentations de valeurs signées peuvent différer selon l'implémentation: complément à un, complément à deux, amplitude du signe. Pour un type non signé, il n'y a aucune raison pour que la norme autorise la variation car il n'y a qu'une seule représentation binaire évidente (la norme autorise uniquement la représentation binaire).
Citations pertinentes:
C99 6.2.6.1:3 :
C99 6.2.6.2:2 :
De nos jours, tous les processeurs utilisent la représentation du complément à deux, mais le dépassement arithmétique signé reste indéfini et les fabricants de compilateurs souhaitent qu'il reste indéfini car ils utilisent cette indéfinie pour aider à l'optimisation. Voir par exemple ce blog de Ian Lance Taylor ou cette plainte d'Agner Fog, et les réponses à son rapport de bug.
la source
Mis à part la bonne réponse de Pascal (dont je suis sûr que c'est la principale motivation), il est également possible que certains processeurs provoquent une exception sur le débordement d'entier signé, ce qui bien sûr causerait des problèmes si le compilateur devait "arranger un autre comportement" ( par exemple, utilisez des instructions supplémentaires pour vérifier les débordements potentiels et calculer différemment dans ce cas).
Il convient également de noter que «comportement indéfini» ne signifie pas «ne fonctionne pas». Cela signifie que l'implémentation est autorisée à faire ce qu'elle veut dans cette situation. Cela implique de faire «la bonne chose» ainsi que «d'appeler la police» ou de «s'écraser». La plupart des compilateurs, lorsque cela est possible, choisissent "faire la bonne chose", en supposant que c'est relativement facile à définir (dans ce cas, c'est le cas). Cependant, si vous rencontrez des débordements dans les calculs, il est important de comprendre ce que cela entraîne réellement et que le compilateur PEUT faire autre chose que ce que vous attendez (et que cela peut très dépendre de la version du compilateur, des paramètres d'optimisation, etc.) .
la source
int f(int x) { return x+1>x; }
avec optimisation. GCC et ICC optimisent les options ci-dessus avecreturn 1;
.int
débordement en fonction des niveaux d'optimisation, voir ideone.com/cki8nM Je pense que cela démontre que votre réponse donne de mauvais conseils.Tout d'abord, veuillez noter que C11 3.4.3, comme tous les exemples et les notes de bas de page, n'est pas un texte normatif et n'est donc pas pertinent à citer!
Le texte pertinent qui déclare que le débordement d'entiers et de flottants est un comportement non défini est le suivant:
C11 6.5 / 5
Une clarification concernant spécifiquement le comportement des types entiers non signés peut être trouvée ici:
C11 6.2.5 / 9
Cela fait des types entiers non signés un cas spécial.
Notez également qu'il existe une exception si un type est converti en type signé et que l'ancienne valeur ne peut plus être représentée. Le comportement est alors simplement défini par l'implémentation, bien qu'un signal puisse être émis.
C11 6.3.1.3
la source
En plus des autres problèmes mentionnés, le fait d'avoir un habillage mathématique non signé fait que les types entiers non signés se comportent comme des groupes algébriques abstraits (ce qui signifie, entre autres, pour toute paire de valeurs
X
etY
, il existera une autre valeurZ
telle queX+Z
, si elle est correctement convertie , égalY
etY-Z
sera, s'il est correctement lancé, égalX
). Si les valeurs non signées étaient simplement des types d'emplacement de stockage et non des types d'expression intermédiaire (par exemple, s'il n'y avait pas d'équivalent non signé du plus grand type entier, et que les opérations arithmétiques sur les types non signés se comportaient comme si elles avaient été converties pour la première fois en types signés plus grands, alors n'aurait pas autant besoin d'un comportement de wrapping défini, mais il est difficile de faire des calculs dans un type qui n'a pas par exemple d'inverse additif.Cela aide dans les situations où le comportement de bouclage est réellement utile - par exemple avec des numéros de séquence TCP ou certains algorithmes, tels que le calcul de hachage. Cela peut également aider dans les situations où il est nécessaire de détecter un débordement, car effectuer des calculs et vérifier s'ils débordent est souvent plus facile que de vérifier à l'avance s'ils débordent, en particulier si les calculs impliquent le plus grand type entier disponible.
la source
a+b-c
est calculée dans une boucle, maisb
etc
sont constants dans cette boucle, il peut être utile de se déplacer calcul de l'(b-c)
extérieur de la boucle, mais faire cela , il faudrait entre autres choses qui(b-c)
donnent une valeur qui, lorsqu'elle est ajoutée àa
, donneraa+b-c
, ce qui nécessite à son tour d'c
avoir un inverse additif.(a+b)-c
égauxa+(b-c)
si oui ou non la valeur arithmétiqueb-c
est représentable dans le type, la substitution sera valable quelle que soit la plage de valeurs possibles pour(b-c)
.Peut-être une autre raison pour laquelle l'arithmétique non signée est définie est parce que les nombres non signés forment des entiers modulo 2 ^ n, où n est la largeur du nombre non signé. Les nombres non signés sont simplement des nombres entiers représentés en utilisant des chiffres binaires au lieu de chiffres décimaux. La réalisation des opérations standard dans un système de module est bien connue.
La citation de l'OP fait référence à ce fait, mais met également en évidence le fait qu'il n'y a qu'une seule façon logique et non ambiguë de représenter des entiers non signés en binaire. En revanche, les nombres signés sont le plus souvent représentés en utilisant le complément à deux mais d'autres choix sont possibles comme décrit dans la norme (section 6.2.6.2).
La représentation du complément à deux permet à certaines opérations d'avoir plus de sens au format binaire. Par exemple, l'incrémentation des nombres négatifs est la même que pour les nombres positifs (attendez-vous dans des conditions de débordement). Certaines opérations au niveau de la machine peuvent être identiques pour les numéros signés et non signés. Cependant, lors de l'interprétation du résultat de ces opérations, certains cas n'ont pas de sens - débordement positif et négatif. De plus, les résultats de dépassement diffèrent selon la représentation sous-jacente signée.
la source