Arithmetic operators
Les opérateurs arithmétiques appliquent des opérations mathématiques standard à leurs opérandes.
|
Cette section est incomplète
Motif : envisager un sommaire plus polyvalent pour ce tableau et d'autres couvrant plusieurs sujets |
| Opérateur | Nom de l'opérateur | Exemple | Résultat |
|---|---|---|---|
| + | plus unaire | + a | la valeur de a après promotions |
| - | moins unaire | - a | l'opposé 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 |
| % | reste | a % b | le reste de a divisé par b |
| ~ | NON bit à bit | ~a | le NON bit à bit de a |
| & | ET bit à bit | a & b | le ET bit à bit de a et b |
| | | OU bit à bit | a | b | le OU bit à bit de a et b |
| ^ | OU exclusif bit à bit | a ^ b | le OU exclusif bit à bit de a et b |
| << | décalage à gauche | a << b | a décalé à gauche de b |
| >> | décalage à droite | a >> b | a décalé à droite de b |
Table des matières |
Dépassements
L'arithmétique des entiers non signés est toujours effectuée
modulo 2
n
où n est le nombre de bits dans cet entier particulier. Par exemple, pour
unsigned
int
, ajouter un à
UINT_MAX
donne
0
, et soustraire un de
0
donne
UINT_MAX
.
Lorsqu'une opération arithmétique sur des entiers signés dépasse la capacité (le résultat ne tient pas dans le type de résultat), le comportement est indéfini : il peut s'enrouler selon les règles de la représentation (généralement le complément à 2), il peut déclencher un piège sur certaines plateformes ou via des options de compilation (par exemple
-ftrapv
dans GCC et Clang), ou peut être complètement
optimisé par le compilateur
.
Environnement de virgule flottante
Si
#pragma STDC FENV_ACCESS
est défini sur
ON
, tous les opérateurs arithmétiques à virgule flottante respectent la
direction d'arrondi
actuelle et signalent les erreurs arithmétiques à virgule flottante comme spécifié dans
math_errhandling
, sauf s'ils font partie d'un
initialiseur statique
(auquel cas les exceptions virgule flottante ne sont pas levées et le mode d'arrondi est vers le plus proche).
Contraction en virgule flottante
Sauf si
#pragma STDC FP_CONTRACT
est défini sur
OFF
, tous les calculs en virgule flottante peuvent être effectués 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 écrite. Par exemple, permet l'implémentation de
(
x
*
y
)
+
z
avec une seule instruction CPU de multiplication-addition fusionnée ou l'optimisation de
a
=
x
*
x
*
x
*
x
;
en
tmp
=
x
*
x
;
a
=
tmp
*
tmp
.
Sans rapport avec la contractualisation, les résultats intermédiaires des opérations en virgule flottante peuvent avoir une plage et une précision différentes de celles indiquées par leur type, voir FLT_EVAL_METHOD
Arithmétique unaire
Les expressions d'opérateurs arithmétiques unaires ont la forme
+
expression
|
(1) | ||||||||
-
expression
|
(2) | ||||||||
| expression | - | expression de tout arithmetic type |
L'opérateur plus unaire et l'opérateur moins unaire appliquent d'abord les promotions intégrales à leur opérande, puis
- le plus unaire renvoie la valeur après promotion
- le moins unaire renvoie la négation de la valeur après promotion (sauf que la négation 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
L'opérateur unaire moins provoque un comportement indéfini en raison d'un dépassement 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 <stdio.h> #include <complex.h> #include <limits.h> int main(void) { char c = 'a'; printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c); printf("-1, where 1 is signed: %d\n", -1); // Comportement défini car l'arithmétique est effectuée pour un entier non signé. // Ainsi, le calcul est (-1) modulo (2 puissance n) = UINT_MAX, où n est // le nombre de bits de unsigned int. Si unsigned int est sur 32 bits, alors // cela donne (-1) modulo (2 puissance 32) = 4294967295 printf("-1, where 1 is unsigned: %u\n", -1u); // Comportement indéfini car la valeur mathématique de -INT_MIN = INT_MAX + 1 // (c'est-à-dire 1 de plus que la valeur maximale possible pour signed int) // // printf("%d\n", -INT_MIN); // Comportement indéfini car la valeur mathématique de -LONG_MIN = LONG_MAX + 1 // (c'est-à-dire 1 de plus que la valeur maximale possible pour signed long) // // printf("%ld\n", -LONG_MIN); // Comportement indéfini car la valeur mathématique de -LLONG_MIN = LLONG_MAX + 1 // (c'est-à-dire 1 de plus que la valeur maximale possible pour signed long long) // // printf("%lld\n", -LLONG_MIN); double complex z = 1 + 2*I; printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z)); }
Sortie possible :
sizeof char: 1 sizeof int: 4 -1, where 1 is signed: -1 -1, where 1 is unsigned: 4294967295 -(1+2i) = -1.0-2.0
Opérateurs additifs
Les expressions d'opérateur arithmétique additif binaire ont la forme
lhs
+
rhs
|
(1) | ||||||||
lhs
-
rhs
|
(2) | ||||||||
-
- les deux ont des types arithmétiques , y compris les types complexes et imaginaires
- l'un est un pointeur vers un type d'objet complet, l'autre a un type entier
-
- les deux ont des types arithmétiques , incluant les types complexes et imaginaires
- lhs a un pointeur vers un type objet complet, rhs a un type entier
- les deux sont des pointeurs vers des objets complets de types compatibles , en ignorant les qualificateurs
Addition et soustraction arithmétiques
Si les deux opérandes ont des types arithmétiques , alors
- d'abord, usual arithmetic conversions sont effectuées
- ensuite, les valeurs des opérandes après conversion sont additionnées ou soustraites selon les règles mathématiques usuelles (pour la soustraction, rhs est soustrait de lhs ), sauf que
-
- si un opérande est NaN, le résultat est NaN
- l'infini moins l'infini est NaN et FE_INVALID est déclenché
- l'infini plus l'infini négatif est NaN et FE_INVALID est déclenché
L'addition et la soustraction des nombres complexes et imaginaires sont définies comme suit (notez que le type du 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 usuelles) :
| + 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
Ppointe vers un élément d'un tableau avec l'indexI, alors
-
-
P
+
N
et
N
+
P
sont des pointeurs qui pointent vers un élément du même tableau avec l'index
I+N -
P
-
N
est un pointeur qui pointe vers un élément du même tableau avec l'index
I-N
-
P
+
N
et
N
+
P
sont des pointeurs qui pointent vers un élément du même tableau avec l'index
Le comportement est défini uniquement si le pointeur original et le pointeur résultant pointent tous deux vers des éléments du même tableau ou un élément au-delà de la fin de ce tableau. Notez que l'exécution de p-1 lorsque p pointe vers le premier élément d'un tableau est un comportement non défini et peut échouer sur certaines plateformes.
-
Si le pointeur
P1pointe vers un élément d'un tableau avec l'indexI(ou un après la fin) etP2pointe vers un élément du même tableau avec l'indexJ(ou un après la fin), alors
-
- P1 - P2 a une valeur égale à I - J et le type ptrdiff_t (qui est un type entier signé, typiquement deux fois plus petit que la taille du plus grand objet pouvant être déclaré)
Le comportement est défini uniquement si le résultat tient dans ptrdiff_t .
À des fins d'arithmétique des pointeurs, un pointeur vers un objet qui n'est pas un élément d'un tableau est traité comme un pointeur vers le premier élément d'un tableau de taille 1.
// work in progress int n = 4, m = 3; int a[n][m]; // VLA of 4 VLAs of 3 ints each int (*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 d'opérateurs arithmétiques multiplicatifs binaires ont la forme
lhs
*
rhs
|
(1) | ||||||||
lhs
/
rhs
|
(2) | ||||||||
lhs
%
rhs
|
(3) | ||||||||
- d'abord, usual arithmetic conversions sont effectuées. Ensuite...
Multiplication
L'opérateur binaire * effectue la multiplication de ses opérandes (après les conversions arithmétiques habituelles) selon les définitions arithmétiques usuelles, sauf que
- 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 déclenché
- la multiplication de l'infini par un non nul donne l'infini (même pour les arguments complexes)
Parce qu'en C, toute valeur complexe avec au moins une partie infinie est une infini même si son autre partie est un NaN, les règles arithmétiques habituelles ne s'appliquent pas à la multiplication complexe-complexe. Les 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 |
En plus de la gestion de l'infini, la multiplication complexe n'est pas autorisée à provoquer un dépassement de capacité des résultats intermédiaires, sauf lorsque
#pragma STDC CX_LIMITED_RANGE
est défini sur
ON
, auquel cas la valeur peut être calculée comme si par
(x+iy)×(u+iv) = (xu-yv)+i(yu+xv)
, car le programmeur assume la responsabilité de limiter la plage des opérandes et de gérer les infinis.
Malgré l'interdiction des débordements excessifs, la multiplication complexe peut déclencher des exceptions de virgule flottante fallacieuses (autrement, il serait excessivement difficile d'implémenter des versions sans débordement)
#include <stdio.h> #include <stdio.h> #include <complex.h> #include <math.h> int main(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 infinity printf("%f + i*%f\n", creal(z), cimag(z)); // textbook formula would give // cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN // but C gives ±∞+i*nan double complex y = cexp(INFINITY + I*NAN); printf("%f + i*%f\n", 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) conformément aux définitions arithmétiques standard, sauf que
- lorsque le type après les 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'en 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 un infini complexe et le second opérande est fini, alors le résultat de l'opérateur
/est un infini complexe -
si le premier opérande est fini et le second opérande est un infini complexe, alors le résultat de l'opérateur
/est un zéro.
Parce qu'en C, toute valeur complexe avec au moins une partie infinie est considérée comme une infini même si son autre partie est un NaN, les règles arithmétiques habituelles ne s'appliquent pas à la division complexe-complexe. Les 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) |
En plus de la gestion de l'infini, la division complexe n'est pas autorisée à provoquer un dépassement des résultats intermédiaires, sauf lorsque
#pragma STDC CX_LIMITED_RANGE
est défini 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 gérer les infinis.
Malgré l'interdiction des débordements excessifs, la division des nombres complexes peut déclencher des exceptions en virgule flottante non pertinentes (sinon il est excessivement difficile d'implémenter des versions sans débordement)
Si le deuxième opérande est zéro, le comportement est indéfini, sauf si l'arithmétique à virgule flottante IEEE est prise en charge et que la division à virgule flottante a lieu, alors
- Diviser un nombre non nul par ±0,0 donne l'infini de signe correct et FE_DIVBYZERO est déclenché
- Diviser 0,0 par 0,0 donne NaN et FE_INVALID est déclenché
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 deuxième opérande est zéro, le comportement est indéfini.
Si le quotient
a/b
n'est pas représentable dans le type de résultat, le comportement de
a/b
et
a%b
est indéfini (ce qui signifie que
INT_MIN
%-
1
est indéfini sur les systèmes en complément à deux)
Remarque : l'opérateur de reste ne fonctionne pas sur les types à virgule flottante, la fonction de bibliothèque fmod fournit cette fonctionnalité.
Logique bit à bit
Les expressions d'opérateurs arithmétiques bit à bit ont la forme
~
rhs
|
(1) | ||||||||
lhs
&
rhs
|
(2) | ||||||||
lhs
|
rhs
|
(3) | ||||||||
lhs
^
rhs
|
(4) | ||||||||
où
| lhs , rhs | - | expressions de type entier |
Premièrement, les opérateurs & , ^ , et | effectuent les conversions arithmétiques usuelles sur les deux opérandes, et l'opérateur ~ effectue les promotions entières sur son unique opérande.
Ensuite, les opérateurs logiques binaires correspondants sont appliqués bit à bit ; c'est-à-dire que chaque bit du résultat est défini ou effacé selon l'opération logique (NOT, AND, OR ou XOR), appliquée aux bits correspondants des opérandes.
Note : les opérateurs bit à bit sont couramment utilisés pour manipuler des ensembles de bits et des masques binaires.
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 .
Sortie possible :
Promoted mask: 0x000000f0 Value: 0x12345678 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x00000070
Opérateurs de décalage
Les expressions d'opérateur de décalage bit à bit ont la forme
lhs
<<
rhs
|
(1) | ||||||||
lhs
>>
rhs
|
(2) | ||||||||
où
| lhs , rhs | - | expressions de type entier |
Premièrement, les promotions entières sont effectuées individuellement sur chaque opérande (Note : ceci diffère des autres opérateurs arithmétiques binaires, qui effectuent tous les conversions arithmétiques usuelles). Le type du résultat est le type de lhs après promotion.
Le comportement n'est pas défini si rhs est négatif ou s'il est supérieur ou égal au nombre de bits dans le lhs promu.
Pour les
lhs
non signés, la valeur de
LHS << RHS
est la valeur de
LHS * 2
RHS
, réduite modulo la valeur maximale du type de retour plus 1 (c'est-à-dire qu'un décalage bit à bit vers la gauche est effectué et les bits qui sont décalés hors du type de destination sont ignorés). Pour les
lhs
signés avec des valeurs non négatives, la valeur de
LHS << RHS
est
LHS * 2
RHS
si elle est représentable dans le type promu de
lhs
, sinon le comportement est indéfini.
Pour les
lhs
non signés et pour les
lhs
signés avec des valeurs non négatives, la valeur de
LHS >> RHS
est la partie entière de
LHS / 2
RHS
. Pour les
LHS
négatifs, la valeur de
LHS >> RHS
est définie par l'implémentation, où dans la plupart des implémentations, cela effectue un décalage arithmétique à droite (de sorte que le résultat reste négatif). Ainsi dans la plupart des implémentations, le décalage à droite d'un
LHS
signé remplit les nouveaux bits de poids fort avec le bit de signe original (c'est-à-dire avec 0 s'il était non négatif et 1 s'il était négatif).
#include <stdio.h> enum {ONE=1, TWO=2}; int main(void) { char c = 0x10; unsigned long long ulong_num = 0x123; printf("0x123 << 1 = %#llx\n" "0x123 << 63 = %#llx\n" // overflow truncates high bits for unsigned numbers "0x10 << 10 = %#x\n", // char is promoted to int ulong_num << 1, ulong_num << 63, c << 10); long long long_num = -1000; printf("-1000 >> 1 = %lld\n", long_num >> ONE); // implementation defined }
Sortie possible :
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
Références
- Norme C17 (ISO/CEI 9899:2018) :
-
- 6.5.3.3 Opérateurs arithmétiques unaires (p: 64)
-
- 6.5.5 Opérateurs multiplicatifs (p: 66)
-
- 6.5.6 Opérateurs additifs (p: 66-68)
-
- 6.5.7 Opérateurs de décalage binaire (p: 68)
-
- 6.5.10 Opérateur ET binaire (p: 70)
-
- 6.5.11 Opérateur OU exclusif binaire (p: 70)
-
- 6.5.12 Opérateur OU inclusif binaire (p: 70-71)
- 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 binaire (p: 94-95)
-
- 6.5.10 Opérateur ET binaire (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/CEI 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 binaire (p : 84-85)
-
- 6.5.10 Opérateur ET binaire (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 binaire
-
- 3.3.10 Opérateur ET binaire
-
- 3.3.11 Opérateur OU exclusif binaire
-
- 3.3.12 Opérateur OU inclusif binaire
Voir aussi
| Opérateurs courants | ||||||
|---|---|---|---|---|---|---|
| affectation |
incrémentation
décrémentation |
arithmétique | logique | comparaison | accès membre | autres |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
b
]
|
a
(
...
)
|
|
Documentation C++
pour
Opérateurs arithmétiques
|