Nous vous suggérons de tester cette résolution dans un environnement contrôlé avant de l'envoyer en production, bravo.
Les opérateurs arithmétiques appliquent des opérations mathématiques standard à leurs opérandes.
Opérateur | Nom de l'opérateur | Exemple | Résultat |
---|---|---|---|
+ |
unaire plus | +a |
la valeur de a après les promotions |
- |
unaire moins | -a |
le négatif de a |
+ |
addition | a + b |
l'addition de a et b |
- |
soustraction | a - b |
la soustraction de b de a |
* |
produit | a * b |
le produit de a et b |
/ |
division | a / b |
la division de a par b |
% |
modulo | a % b |
le reste de a divisé par b |
~ |
NON par bit | ~a |
le NOT bit à bit de a |
& |
ET au sens du bit | a & b |
le ET binaire de a et b |
| |
OU par bit | a | b |
le OU bit à bit de a et b |
^ |
XOR au sens du bit | a ^ b |
le XOR au niveau des bits de a et b |
<< |
décalage à gauche par bit | a << b |
a décalé à gauche par b |
>> |
décalage à droite par bit | a >> b |
a décalé à droite par b |
Débordements
L'arithmétique des entiers non signés est toujours effectuée modulo 2.n
où n est le nombre de bits de ce nombre entier particulier. Par exemple, pour unsigned int
on ajoute un à UINT_MAX
donne 0
et en soustrayant un à 0
donne UINT_MAX
.
Lorsque l'opération arithmétique sur les entiers signés déborde (le résultat ne rentre pas dans le type de résultat), le comportement est indéfini : il peut s'enrouler selon les règles de la représentation (typiquement le complément à 2), il peut se piéger sur certaines plateformes ou en raison des options du compilateur (par ex. -ftrapv
dans GCC et Clang), ou peut être complètement optimisé par le compilateur.
Environnement à virgule flottante
Si
est réglé sur ON
, tous les opérateurs arithmétiques en virgule flottante obéissent à la direction d'arrondi en virgule flottante actuelle et signalent les erreurs arithmétiques en virgule flottante comme spécifié dans la section math_errhandling
sauf s'ils font partie d'un initialisateur statique (auquel cas les exceptions en virgule flottante ne sont pas levées et le mode d'arrondi est au plus proche).
Contraction en virgule flottante
Sauf si #pragma STDC FP_CONTRACT
ne soit réglé sur OFF
, toute l'arithmétique en virgule flottante peut être effectuée comme si les résultats intermédiaires avaient une plage et une précision infinies, c'est-à-dire des optimisations qui omettent les erreurs d'arrondi et les exceptions en virgule flottante qui seraient observées si l'expression était évaluée exactement comme elle est écrite. Par exemple, permet l'implémentation de (x*y) + z
avec une seule instruction de multiplicateur-additionneur fusionné du CPU ou l'optimisation de a = x*x*x*x;
comme tmp = x*x; a = tmp*tmp
.
Sans rapport avec la contractualisation, les résultats intermédiaires de l'arithmétique à virgule flottante peuvent avoir une plage et une précision différentes de celles indiquées par son type, cf. FLT_EVAL_METHOD
.
Arithmétique unaire
Les expressions des opérateurs arithmétiques unaires ont la forme.
+ expression |
(1) | |
- expression |
(2) |
1) unaire plus (promotion)2) unaire moins (négation)
où.
expression | - | expression de tout type arithmétique |
Le plus unaire et le moins unaire appliquent d'abord des promotions intégrales à leur opérande, puis.
- le plus unaire retourne la valeur après la promotion
- unary minus renvoie le négatif de la valeur après promotion (sauf que le négatif d'un NaN est un autre NaN).
Le type de l'expression est le type après promotion, et la catégorie de valeur est non-lvalue.
Notes
Le moins unaire invoque un comportement non défini dû à un débordement d'entier signé lorsqu'il est appliqué à... INT_MIN
, LONG_MIN
ou LLONG_MIN
, sur les plateformes typiques (complément à 2).
En C++, l'opérateur unaire + peut également être utilisé avec d'autres types intégrés tels que les tableaux et les fonctions, ce qui n'est pas le cas en C.
#include#include intmain(void){char c ='a';printf("sizeof char: %zu sizeof int: %zun",sizeof c,sizeof+c);printf("-1, where 1 is signed: %dn",-1);printf("-1, where 1 is unsigned: %un",-1u);double complex z =1+2*I;printf("-(1+2i) = %.1f%+.1fn",creal(-z),cimag(-z));}
Sortie possible :
sizeofchar:1sizeofint:4-1, where 1 is signed:-1-1, where 1 is unsigned:4294967295-(1+2i)=-1.0-2.0
Opérateurs additifs
Les expressions des opérateurs arithmétiques additifs binaires ont la forme.
lhs+ rhs |
(1) | |
lhs- rhs |
(2) |
1) addition : lhs et rhs doit être l'un des éléments suivants
- les deux ont des types arithmétiques, y compris complexes et imaginaires
- l'un est un pointeur vers un type d'objet complet, l'autre a un type entier
2) soustraction : lhs et rhs doit être l'un des éléments suivants
- les deux ont des types arithmétiques, y compris complexes et imaginaires
- lhs a un pointeur vers un type d'objet complet, rhs a un type entier
- les deux sont des pointeurs vers des objets complets de types compatibles, en ignorant les qualificatifs.
Addition et soustraction arithmétiques
Si les deux opérandes ont des types arithmétiques, alors.
- d'abord, les conversions arithmétiques habituelles sont effectuées.
- ensuite, les valeurs des opérandes après conversions sont additionnées ou soustraites selon les règles habituelles des mathématiques (pour la soustraction, rhs est soustrait de lhs), sauf que
- si un des opérandes est NaN, le résultat est NaN.
- l'infini moins l'infini est NaN et
FE_INVALID
est élevé - l'infini plus l'infini négatif est NaN et
FE_INVALID
est élevé
L'addition et la soustraction complexes et imaginaires sont définies comme suit (notez que le type de résultat est imaginaire si les deux opérandes sont imaginaires et complexe si un opérande est réel et l'autre imaginaire, comme spécifié par les conversions arithmétiques habituelles) :
+ ou - | u | iv | u + iv |
---|---|---|---|
x | x ± u | x ± iv | (x ± u) ± iv |
iy | ±u + iy | i(y ± v) | ±u + i(y ± v) |
x + iy | (x ± u) + iy | x + i(y ± v) | (x ± u) + i(y ± v) |
// work in progress// note: take part of the c/language/conversion example
Arithmétique des pointeurs
- Si le pointeur
P
pointe sur un élément d'un tableau dont l'indiceI
alors P+N
etN+P
sont des pointeurs qui pointent sur un élément du même tableau avec l'indexI+N
P-N
est un pointeur qui pointe sur un élément du même tableau avec l'index {tt|I-N}}.
Ce comportement n'est défini que si le pointeur d'origine et le pointeur de résultat pointent tous deux sur des éléments du même tableau ou sur un élément situé au-delà de la fin de ce tableau. Notez que l'exécution de p-1 lorsque p pointe sur le premier élément d'un tableau est un comportement non défini et peut échouer sur certaines plateformes.
- Si le pointeur
P1
pointe sur un élément d'un tableau dont l'indiceI
(ou un après la fin) etP2
pointe sur un élément du même tableau avec l'indiceJ
(ou un élément après la fin), alors P1-P2
a une valeur égale àJ-I
et le typeptrdiff_t
(qui est un type d'entier signé, généralement deux fois moins grand que la taille du plus grand objet qui peut être déclaré).
Le comportement est défini uniquement si le résultat s'inscrit dans le type ptrdiff_t
.
Pour les besoins de l'arithmétique des pointeurs, un pointeur vers un objet qui n'est un élément d'aucun tableau est traité comme un pointeur vers le premier élément d'un tableau de taille 1.
// work in progressint n =4, m =3;int a[n][m];// VLA of 4 VLAs of 3 ints eachint(*p)[m]= a;// p == &a[0] p = p +1;// p == &a[1] (pointer arithmetic works with VLAs just the same)(*p)[2]=99;// changes a[1][2]
Opérateurs multiplicatifs
Les expressions des opérateurs arithmétiques multiplicatifs binaires ont la forme.
lhs* rhs |
(1) | |
lhs/ rhs |
(2) | |
lhs% rhs |
(3) |
1) multiplication. lhs et rhs doivent avoir des types arithmétiques 2) division. lhs et rhs doivent avoir des types arithmétiques 3) reste. lhs et rhs doivent avoir des types entiers
- d'abord, les conversions arithmétiques habituelles sont effectuées. Ensuite...
Multiplication
L'opérateur binaire * effectue la multiplication de ses opérandes (après les conversions arithmétiques habituelles) en suivant les définitions arithmétiques habituelles, à l'exception de ce qui suit .
- si un opérande est un NaN, le résultat est un NaN.
- la multiplication de l'infini par zéro donne NaN et
FE_INVALID
est soulevée - multiplication de l'infini par un non nul donne l'infini (même pour les arguments complexes).
Parce qu'en C, toute valeur complexe ayant au moins une partie infinie comme un infini même si son autre partie est un NaN, les règles arithmétiques habituelles ne s'appliquent pas à la multiplication complexe-complexe. D'autres combinaisons d'opérandes flottants suivent le tableau suivant :
* | u | iv | u + iv |
---|---|---|---|
x | xu | i(xv) | (xu) + i(xv) |
iy | i(yu) | -yv | (-yv) + i(yu) |
x + iy | (xu) + i(yu) | (-yv) + i(xv) | règles spéciales |
Outre la gestion de l'infini, la multiplication complexe n'est pas autorisée à faire déborder les résultats intermédiaires, sauf lorsque #pragma STDC CX_LIMITED_RANGE
est réglé sur ON
, auquel cas la valeur peut être calculée comme si par (x+iy)×(u+iv) = (xu-yv)+i(yu+xv), le programmeur assumant la responsabilité de limiter la plage des opérandes et de traiter les infinis.
Malgré l'interdiction du débordement indu, la multiplication complexe peut soulever des exceptions fallacieuses en virgule flottante (sinon, il est prohibitivement difficile d'implémenter des versions sans débordement).
#include#include #include #include intmain(void){// TODO simpler cases, take some from C++double complex z =(1+0*I)*(INFINITY + I*INFINITY);// textbook formula would give// (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN// but C gives a complex infinityprintf("%f + i*%fn",creal(z),cimag(z));// textbook formula would give// cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN// but C gives ±∞+i*nandouble complex y =cexp(INFINITY + I*NAN);printf("%f + i*%fn",creal(y),cimag(y));}
Sortie possible :
inf + i*inf inf + i*nan
Division
L'opérateur binaire / divise le premier opérande par le second (après les conversions arithmétiques habituelles) en suivant les définitions arithmétiques habituelles, sauf que.
- lorsque le type après conversions arithmétiques usuelles est un type entier, le résultat est le quotient algébrique (et non une fraction), arrondi dans une direction définie par l'implémentation.(jusqu'à C99)tronqué vers zéro(depuis C99)
- si un opérande est un NaN, le résultat est un NaN
- si le premier opérande est une infinité complexe et que le second opérande est fini, alors le
Le résultat de l'opérateur / est un infini complexe.
- si le premier opérande est fini et que le second opérande est une infinité complexe, alors l'opérateur
résultat de l'opérateur / est un zéro.
Parce qu'en C, toute valeur complexe avec au moins une partie infinie comme une infinité même si son autre partie est un NaN, les règles arithmétiques habituelles ne s'appliquent pas à la division complexe-complexe. D'autres combinaisons d'opérandes flottants suivent le tableau suivant :
/ | u | iv |
---|---|---|
x | x/u | i(-x/v) |
iy | i(y/u) | y/v |
x + iy | (x/u) + i(y/u) | (y/v) + i(-x/v) |
Outre la gestion de l'infini, la division complexe n'est pas autorisée à faire déborder les résultats intermédiaires, sauf lorsque #pragma STDC CX_LIMITED_RANGE
est réglé sur ON
, auquel cas la valeur peut être calculée comme si par (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u 2
+v 2
), car le programmeur assume la responsabilité de limiter la plage des opérandes et de traiter les infinis.
Malgré l'interdiction du débordement indu, la division complexe peut soulever des exceptions fallacieuses en virgule flottante (sinon, il est excessivement difficile d'implémenter des versions sans débordement).
Si le second opérande est zéro, le comportement est indéfini, sauf que si l'arithmétique à virgule flottante IEEE est supportée, et que la division en virgule flottante a lieu, alors .
- La division d'un nombre non nul par ±0,0 donne l'infini correctement signé et...
FE_DIVBYZERO
est relevé - La division de 0,0 par 0,0 donne NaN et
FE_INVALID
est soulevé
Reste
L'opérateur binaire % donne le reste de la division du premier opérande par le second (après les conversions arithmétiques habituelles).
Le signe du reste est défini de telle sorte que si le quotient a/b
est représentable dans le type de résultat, alors (a/b)*b + a%b == a
.
Si le second opérande est nul, le comportement est indéfini.
Si le quotient a/b
n'est pas représentable dans le type de résultat, le comportement des deux opérations a/b
et de a%b
est indéfini (ce qui signifie que INT_MIN%-1
est indéfini sur les systèmes de complément à 2).
Note : l'opérateur de reste ne fonctionne pas sur les types à virgule flottante, la fonction de la bibliothèque. fmod
fournit cette fonctionnalité.
Logique par bit
Les expressions de l'opérateur arithmétique par bit ont la forme.
~ rhs |
(1) | |
lhs& rhs |
(2) | |
lhs| rhs |
(3) | |
lhs^ rhs |
(4) |
1) bitwise NOT 2) ET par bit 3) OU binaire 4) XOR au sens des bits
où.
lhs, rhs | - | expressions de type entier |
Tout d'abord, les opérateurs &, ^ et | effectuent des conversions arithmétiques habituelles sur les deux opérandes et l'opérateur ~ effectue des promotions d'entiers sur son seul opérande.
Ensuite, les opérateurs logiques binaires correspondants sont appliqués bitwis.e; c'est-à-dire que chaque bit du résultat est activé ou désactivé selon l'opération logique (NOT, AND, OR, ou XOR), appliquée aux bits correspondants des opérandes.
Remarque : les opérateurs de type bit sont couramment utilisés pour manipuler les ensembles de bits et les masques de bits.
Note : pour les types non signés (après promotion), l'expression ~E est équivalente à la valeur maximale représentable par le type de résultat moins la valeur originale de E.
#include#include intmain(void){uint16_t mask =0x00f0;uint32_t a =0x12345678;printf("Value: %#x mask: %#xn""Setting bits: %#xn""Clearing bits: %#xn""Selecting bits: %#xn", a,mask,(a|mask),(a&~mask),(a&mask));}
Sortie possible :
Value:0x12345678 mask:0xf0 Setting bits:0x123456f8 Clearing bits:0x12345608 Selecting bits:0x70
Opérateurs de décalage
Les expressions de l'opérateur de décalage au sens du bit ont la forme.
lhs<< rhs |
(1) | |
lhs>> rhs |
(2) |
1) décalage à gauche de lhs par rhs bits 2) décalage vers la droite de lhs par rhs bits
où.
lhs, rhs | - | expressions de type entier |
D'abord, les promotions d'entiers sont effectuées, individuellement, sur chaque opérande (Note : ceci est différent des autres opérateurs arithmétiques binaires, qui effectuent tous des conversions arithmétiques habituelles). Le type du résultat est le type de lhs après la promotion.
Pour les non signés lhs, la valeur de LHS << RHS
est la valeur de LHS * 2 RHS
réduit modulo la valeur maximale du type de retour plus 1 (c'est-à-dire qu'un décalage vers la gauche est effectué et que les bits qui sont décalés hors du type de destination sont rejetés). Pour les données signées lhs avec des valeurs non négatives, la valeur de LHS << RHS
est LHS * 2 RHS
s'il est représentable dans le type promu de lhs, sinon le comportement est indéfini.
Pour les non signés lhs et pour les signés lhs avec des valeurs non négatives, la valeur de LHS >> RHS
est la partie entière de LHS / 2 RHS
. Pour les négatifs LHS
la valeur de LHS >> RHS
est défini par l'implémentation. Dans la plupart des implémentations, il effectue un décalage arithmétique vers la droite (de sorte que le résultat reste négatif). Ainsi, dans la plupart des implémentations, le décalage vers la droite d'un signe LHS
remplit les nouveaux bits d'ordre supérieur avec le bit de signe original (c'est-à-dire avec 0 s'il était non-négatif et 1 s'il était négatif).
Dans tous les cas, le comportement est indéfini si rhs est négatif ou est supérieur ou égal au nombre de bits dans le promu. lhs.
#includeenum{ONE=1, TWO=2};intmain(void){char c =0x10;unsignedlonglong ulong_num =0x123;printf("0x123 << 1 = %#llxn""0x123 << 63 = %#llxn"// overflow truncates high bits for unsigned numbers"0x10 << 10 = %#xn",// char is promoted to int ulong_num <<1, ulong_num <<63, c <<10);longlong long_num =-1000;printf("-1000 >> 1 = %lldn", long_num >> ONE);// implementation defined}
Sortie possible :
0x123<<1=0x2460x123<<63=0x80000000000000000x10<<10=0x4000-1000>>1=-500
Références :
- Norme C11 (ISO/IEC 9899:2011) :
- 6.5.3.3 Opérateurs arithmétiques unaires (p : 89)
- 6.5.5 Opérateurs multiplicatifs (p : 92)
- 6.5.6 Opérateurs additifs (p : 92-94)
- 6.5.7 Opérateurs de décalage par bit (p : 94-95)
- 6.5.10 Opérateur AND par bit (p : 97)
- 6.5.11 Opérateur OU exclusif binaire (p : 98)
- 6.5.12 Opérateur OU inclusif binaire (p : 98)
- Norme C99 (ISO/IEC 9899:1999) :
- 6.5.3.3 Opérateurs arithmétiques unaires (p : 79)
- 6.5.5 Opérateurs multiplicatifs (p : 82)
- 6.5.6 Opérateurs additifs (p : 82-84)
- 6.5.7 Opérateurs de décalage par bit (p : 84-85)
- 6.5.10 Opérateur AND par bit (p : 87)
- 6.5.11 Opérateur OU exclusif binaire (p : 88)
- 6.5.12 Opérateur OU inclusif binaire (p : 88)
- Norme C89/C90 (ISO/IEC 9899:1990) :
- 3.3.3.3 Opérateurs arithmétiques unaires
- 3.3.5 Opérateurs multiplicatifs
- 3.3.6 Opérateurs additifs
- 3.3.7 Opérateurs de décalage par bit
- 3.3.10 Opérateur AND par bit
- 3.3.11 Opérateur OU exclusif binaire
- 3.3.12 Opérateur OU inclusif binaire
Voir aussi
Précédence de l'opérateur.
Opérateurs communs | ||||||
---|---|---|---|---|---|---|
affectation | incrémenter décrémenter |
arithmétique | logique | comparaison | membre accès |
autre |
|
|
|
|
|
|
|
Si vous avez la moindre méfiance ou moyen d'augmenter notre actualité, nous vous incitons à écrire un commentaire et nous l'étudierons avec plaisir.