J'ai rencontré du code de quelqu'un qui semble croire qu'il y a un problème pour soustraire un entier non signé d'un autre entier du même type lorsque le résultat serait négatif. Ce code comme celui-ci serait donc incorrect même s'il fonctionnait sur la plupart des architectures.
unsigned int To, Tf;
To = getcounter();
while (1) {
Tf = getcounter();
if ((Tf-To) >= TIME_LIMIT) {
break;
}
}
C'est la seule citation vaguement pertinente de la norme C que j'ai pu trouver.
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 un supérieur à la plus grande valeur pouvant être représentée par le type résultant.
Je suppose que l'on pourrait prendre cette citation comme signifiant que lorsque l'opérande droit est plus grand, l'opération est ajustée pour être significative dans le contexte des nombres tronqués modulo.
c'est à dire
0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF
au lieu d'utiliser la sémantique signée dépendante de l'implémentation:
0x0000 - 0x0001 == (non signé) (0 + -1) == (0xFFFF mais aussi 0xFFFE ou 0x8001)
Quelle ou quelle interprétation est la bonne? Est-il défini du tout?
Réponses:
Le résultat d'une soustraction générant un nombre négatif dans un type non signé est bien défini:
Comme vous pouvez le voir,
(unsigned)0 - (unsigned)1
égal à -1 modulo UINT_MAX + 1, ou en d'autres termes, UINT_MAX.Notez que bien qu'il dise "Un calcul impliquant des opérandes non signés ne peut jamais déborder", ce qui pourrait vous amener à croire qu'il ne s'applique que pour le dépassement de la limite supérieure, cela est présenté comme une motivation pour la partie de liaison réelle de la phrase: "a le 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 de un à la plus grande valeur pouvant être représentée par le type résultant. " Cette phrase n'est pas limitée au dépassement de la limite supérieure du type, et s'applique également aux valeurs trop faibles pour être représentées.
la source
uint
c'était toujours destiné à représenter l' anneau mathématique des entiers à0
traversUINT_MAX
, avec les opérations d'addition et de multiplication moduloUINT_MAX+1
, et non parce que d'un débordement. Cependant, cela soulève la question de savoir pourquoi, si les anneaux sont un type de données si fondamental, le langage n'offre pas un support plus général pour les anneaux d'autres tailles.Lorsque vous travaillez avec des types non signés , l'arithmétique modulaire (également appelée comportement «enveloppant» ) est en cours. Pour comprendre cette arithmétique modulaire , jetez simplement un œil à ces horloges:
9 + 4 = 1 ( 13 mod 12 ), donc dans l'autre sens c'est: 1 - 4 = 9 ( -3 mod 12 ). Le même principe est appliqué lorsque vous travaillez avec des types non signés. Si le type de résultat est
unsigned
, alors l'arithmétique modulaire a lieu.Examinez maintenant les opérations suivantes en stockant le résultat sous forme de
unsigned int
:Lorsque vous voulez vous assurer que le résultat est
signed
, stockez-le dans unesigned
variable ou transtypez-le ensigned
. Lorsque vous souhaitez obtenir la différence entre les nombres et vous assurer que l'arithmétique modulaire ne sera pas appliquée, vous devez envisager d'utiliser laabs()
fonction définie dansstdlib.h
:Soyez très prudent, en particulier lors de l'écriture des conditions, car:
mais
la source
int d = abs(five - seven);
n'est pas bonne. Le premierfive - seven
est calculé: la promotion laisse les types d'opérandes tels quelsunsigned int
, le résultat est calculé modulo(UINT_MAX+1)
et s'évalue àUINT_MAX-1
. Ensuite, cette valeur est le paramètre réel deabs
, ce qui est une mauvaise nouvelle.abs(int)
provoque un comportement non défini passer l'argument, car il est hors de portée, etabs(long long)
peut probablement contenir la valeur, mais un comportement non défini se produit lorsque la valeur de retour est contrainte àint
initialiserd
.operator T()
. L'ajout dans les deux expressions dont nous discutons est effectué en typeunsigned int
, basé sur les types d'opérande. Le résultat de l'addition estunsigned int
. Ensuite, ce résultat est implicitement converti en type requis dans le contexte, une conversion qui échoue car la valeur n'est pas représentable dans le nouveau type.double x = 2/3;
vsdouble y = 2.0/3;
Eh bien, la première interprétation est correcte. Cependant, votre raisonnement sur la «sémantique signée» dans ce contexte est faux.
Encore une fois, votre première interprétation est correcte. L'arithmétique non signée suit les règles de l'arithmétique modulo, ce qui signifie qu'elle est
0x0000 - 0x0001
évaluée0xFFFF
pour les types non signés 32 bits.Cependant, la seconde interprétation (celle basée sur la «sémantique signée») est également nécessaire pour produire le même résultat. C'est-à-dire que même si vous évaluez
0 - 1
dans le domaine du type signé et que vous obtenez-1
le résultat intermédiaire, il-1
est toujours nécessaire de le produire0xFFFF
lorsqu'il sera converti ultérieurement en type non signé. Même si certaines plates-formes utilisent une représentation exotique pour les entiers signés (complément de 1, magnitude signée), cette plate-forme doit toujours appliquer des règles d'arithmétique modulo lors de la conversion de valeurs entières signées en valeurs non signées.Par exemple, cette évaluation
est toujours garanti de produire
UINT_MAX
dansc
, même si la plate-forme utilise une représentation exotique pour les entiers signés.la source
Avec des nombres non signés de type
unsigned int
ou plus, en l'absence de conversions de type,a-b
est défini comme donnant le nombre non signé qui, lorsqu'il est ajoutéb
, donneraa
. La conversion d'un nombre négatif en non signé est définie comme donnant le nombre qui, lorsqu'il est ajouté au nombre original inversé de signe, donnera zéro (donc convertir -5 en non signé donnera une valeur qui, lorsqu'elle est ajoutée à 5, donnera zéro) .Notez que les nombres non signés plus petits que
unsigned int
peuvent être promus en typeint
avant la soustraction, le comportement dea-b
dépendra de la taille deint
.la source