Namespaces
Variants

Arithmetic operators

From cppreference.net

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
+ 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)
1) plus unaire (promotion)
2) moins unaire (négation)
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)
1) addition : lhs et rhs doivent être l'un des suivants
  • 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
2) soustraction : lhs et rhs doivent être l'un des suivants
  • 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 P pointe vers un élément d'un tableau avec l'index I , 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

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 P1 pointe vers un élément d'un tableau avec l'index I (ou un après la fin) et P2 pointe vers un élément du même tableau avec l'index J (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)
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

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)
1) NOT au niveau des bits
2) ET bit à bit
3) OU au niveau des bits
4) bitwise XOR

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 .

#include <stdio.h>
#include <stdint.h>
int main(void)
{
    uint32_t a = 0x12345678;
    uint16_t mask = 0x00f0;
    printf("Promoted mask:\t%#010x\n"
           "Value:\t\t%#x\n"
           "Setting bits:\t%#x\n"
           "Clearing bits:\t%#x\n"
           "Selecting bits:\t%#010x\n"
           , mask
           , a
           , a | mask
           , a & ~mask
           , a & mask
    );
}

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)
1) décalage à gauche de lhs de rhs bits
2) décalage à droite de lhs par rhs bits

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

Priorité des opérateurs

Opérateurs courants
affectation incrémentation
décrémentation
arithmétique logique comparaison accès membre autres

a = b
a + = b
a - = b
a * = b
a / = b
a % = b
a & = b
a | = b
a ^ = b
a <<= b
a >>= b

++ a
-- a
a ++
a --

+ a
- a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

! a
a && b
a || b

a == b
a ! = b
a < b
a > b
a <= b
a >= b

a [ b ]
* a
& a
a - > b
a. b

a ( ... )
a, b
( type ) a
a ? b : c
sizeof


_Alignof
(depuis C11)
(jusqu'à C23)

alignof
(depuis C23)

Documentation C++ pour Opérateurs arithmétiques