web-dev-qa-db-fra.com

Pourquoi le comportement défini de dépassement d'entier non signé est-il défini alors que le dépassement d'entier signé ne l'est pas?

Le dépassement d'entier non signé est bien défini à la fois par les normes C et C++. Par exemple, le norme C99 (§6.2.5/9) États

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 valeur la plus grande pouvant être représentée par le type obtenu.

Cependant, les deux normes indiquent que le dépassement d'entier signé est un comportement indéfini. Encore une fois, de la norme C99 (§3.4.3/1)

Un exemple de comportement indéfini est le comportement lors du dépassement de nombre entier.

Existe-t-il une raison historique ou (encore mieux!) Technique pour expliquer cet écart?

194

La raison historique est que la plupart des implémentations C (compilateurs) n'utilisaient que le comportement de débordement le plus facile à implémenter avec la représentation entière utilisée. Les implémentations en C utilisaient généralement la même représentation que celle utilisée par la CPU - le comportement de débordement était donc identique à celui de la représentation entière utilisée par la CPU.

En pratique, seules les représentations de valeurs signées peuvent différer en fonction de la mise en œuvre: complément à un, complément à deux, grandeur du signe. Pour un type non signé, il n'y a aucune raison pour que le standard autorise les variations car il n'y a qu'une seule représentation binaire évidente (le standard n'autorise que la représentation binaire).

Citations pertinentes:

C99 6.2.6.1:3:

Les valeurs stockées dans des champs de bits non signés et des objets de type unsigned char doivent être représentées à l'aide d'une notation binaire pure.

C99 6.2.6.2:2:

Si le bit de signe est à un, la valeur doit être modifiée de l’une des manières suivantes:

- la valeur correspondante avec le bit de signe 0 est annulée ( signe et magnitude);

- le bit de signe a la valeur 2N) ( complément à deux);

- le bit de signe a la valeur - (2N - 1) ( son complément).


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 veulent qu'il reste indéfini, car ils utilisent cette indétermination pour faciliter l'optimisation. Voir par exemple ceci --- (article de blog de Ian Lance Taylor ou ceci plainte de Agner Fog, et les réponses à son rapport de bogue.

150
Pascal Cuoq

En plus de la bonne réponse de Pascal (dont je suis sûr que c'est la motivation principale), il est également possible que certains processeurs provoquent une exception lors du dépassement d'entier signé, ce qui poserait bien entendu des problèmes si le compilateur devait "organiser un autre comportement" ( par exemple, utilisez des instructions supplémentaires pour vérifier le débordement potentiel et calculez 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 inclut de faire "ce qui est juste" ainsi que "d'appeler la police" ou "se briser". La plupart des compilateurs, dans la mesure du possible, choisiront de "faire le bon choix", en supposant que cela soit 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 dépendre de la version du compilateur, des paramètres d'optimisation, etc.). .

14
Mats Petersson

Tout d'abord, veuillez noter que C11 3.4.3, comme tous les exemples et notes de bas de page, n'est pas un texte normatif et n'est donc pas pertinent à citer!

Le texte pertinent qui indique que le dépassement de capacité des entiers et des flottants est un comportement indéfini est le suivant:

C11 6.5/5

Si une condition exceptionnelle se produit lors de l'évaluation d'une expression (c'est-à-dire si le résultat n'est pas défini mathématiquement ou n'est pas compris dans la plage de valeurs pouvant être représentées pour son type), le comportement est indéfini.

Une clarification concernant le comportement des types entiers non signés peut être trouvée ici:

C11 6.2.5/9

La plage de valeurs non négatives d'un type entier signé est une sous-gamme du type entier non signé correspondant, et la représentation de la même valeur dans chaque type est identique. 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 pouvant être représentée par le type résultant.

Cela fait des types entiers non signés un cas particulier.

Notez également qu'il existe une exception si un type est converti en un type signé et que l'ancienne valeur ne peut plus être représentée. Le comportement est alors simplement défini par la mise en œuvre, bien qu'un signal puisse être émis.

C11 6.3.1.

6.3.1.3 Entiers signés et non signés

Lorsqu'une valeur de type entier est convertie en un type entier autre que _Bool, si la valeur peut être représentée par le nouveau type, elle est inchangée.

Sinon, si le nouveau type n'est pas signé, la valeur est convertie en ajoutant ou en soustrayant de manière répétée une valeur de plus que la valeur maximale pouvant être représentée dans le nouveau type jusqu'à ce que la valeur se situe dans la plage du nouveau type.

Sinon, le nouveau type est signé et la valeur ne peut pas y être représentée. soit le résultat est défini par l'implémentation, soit un signal défini par l'implémentation est émis.

9
Lundin

En plus des autres problèmes mentionnés, avoir un encapsulation mathématique non signée fait en sorte que les types entiers non signés se comportent comme des groupes algébriques abstraits (ce qui signifie, entre autres, que pour toute paire de valeurs X et Y, il existera une autre valeur Z telle que X+Z sera, si correctement converti, égal à Y et Y-Z sera, si correctement converti, égal à X). Si les valeurs non signées sont simplement des types d’emplacement de stockage et non des types d’expression intermédiaire (par exemple, s’il n’existe pas d’équivalent non signé du type entier le plus grand, et que les opérations arithmétiques sur les types non signés se comportent comme si elles avaient été converties au préalable en types signés plus importants, Un comportement d'emballage défini ne serait pas aussi nécessaire, 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 être utile dans les situations où il est nécessaire de détecter les débordements, car il est souvent plus facile d'effectuer des calculs et de vérifier s'ils ont débordé que de vérifier à l'avance s'ils débordent, en particulier si les calculs impliquent le type entier le plus grand disponible.

6
supercat

Peut-être une autre raison pour laquelle l'arithmétique non signée est définie est 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 entiers représentés par des chiffres binaires au lieu de chiffres décimaux. Effectuer les opérations standard dans un système de module est bien compris.

La citation de l'OP fait référence à ce fait, mais souligne également le fait qu'il n'existe qu'un seul moyen 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 par un complément de deux, mais d'autres choix sont possibles, comme indiqué dans la norme (section 6.2.6.2).

La représentation en complément à deux permet à certaines opérations d'avoir plus de sens en format binaire. Par exemple, l'incrémentation de nombres négatifs est la même que pour les nombres positifs (prévoir 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, lorsqu’on interprète le 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ébordement diffèrent selon la représentation sous-jacente signée.

1
yth