Arithmetic operators
Arithmetische Operatoren wenden Standardmathematische Operationen auf ihre Operanden an.
|
Dieser Abschnitt ist unvollständig
Grund: Erwägen Sie einen vielseitigeren Inhaltsverzeichnis für diese und andere Tabellen, die mehrere Themen abdecken |
| Operator | Operatorname | Beispiel | Ergebnis |
|---|---|---|---|
| + | Unäres Plus | + a | Der Wert von a nach Typenerweiterungen |
| - | Unäres Minus | - a | Das Negative von a |
| + | Addition | a + b | Die Addition von a und b |
| - | Subtraktion | a - b | Die Subtraktion von b von a |
| * | Produkt | a * b | Das Produkt von a und b |
| / | Division | a / b | Die Division von a durch b |
| % | Rest | a % b | Der Rest der Division von a durch b |
| ~ | Bitweises NOT | ~a | Das bitweise NOT von a |
| & | Bitweises AND | a & b | Das bitweise AND von a und b |
| | | Bitweises OR | a | b | Das bitweise OR von a und b |
| ^ | Bitweises XOR | a ^ b | Das bitweise XOR von a und b |
| << | Bitweise Linksverschiebung | a << b | a links verschoben um b |
| >> | Bitweise Rechtsverschiebung | a >> b | a rechts verschoben um b |
Inhaltsverzeichnis |
Überläufe
Die Arithmetik vorzeichenloser Ganzzahlen wird immer
modulo 2
n
durchgeführt, wobei n die Anzahl der Bits in dieser bestimmten Ganzzahl ist. Zum Beispiel ergibt für
unsigned
int
das Addieren von eins zu
UINT_MAX
den Wert
0
, und das Subtrahieren von eins von
0
ergibt
UINT_MAX
.
Wenn eine Operation mit vorzeichenbehafteten Ganzzahlen überläuft (das Ergebnis passt nicht in den Ergebnistyp), ist das Verhalten undefiniert: Es kann gemäß den Regeln der Darstellung umschlagen (typischerweise 2er-Komplement), es kann auf einigen Plattformen oder aufgrund von Compiler-Optionen zu einem Trap führen (z.B.
-ftrapv
in GCC und Clang), oder es kann vom Compiler vollständig
optimiert werden
.
Gleitkomma-Umgebung
Wenn
#pragma STDC FENV_ACCESS
auf
ON
gesetzt ist, befolgen alle Gleitkomma-Operatoren die aktuelle
Rundungsrichtung
und melden Gleitkomma-Arithmetikfehler wie in
math_errhandling
spezifiziert, es sei denn, sie sind Teil einer
statischen Initialisierung
(in diesem Fall werden keine Gleitkomma-Ausnahmen ausgelöst und der Rundungsmodus ist "to nearest").
Gleitkomma-Kontraktion
Sofern
#pragma STDC FP_CONTRACT
nicht auf
OFF
gesetzt ist, darf jede Gleitkomma-Operation so ausgeführt werden, als hätten die Zwischenergebnisse unendlichen Wertebereich und unendliche Genauigkeit, d.h. Optimierungen, die Rundungsfehler und Gleitkomma-Ausnahmen unterdrücken, die beobachtet würden, wenn der Ausdruck exakt wie geschrieben ausgewertet würde. Erlaubt beispielsweise die Implementierung von
(
x
*
y
)
+
z
mit einer einzigen Fused-Multiply-Add-CPU-Instruktion oder die Optimierung von
a
=
x
*
x
*
x
*
x
;
zu
tmp
=
x
*
x
;
a
=
tmp
*
tmp
.
Unabhängig von der Vertragsgestaltung können Zwischenergebnisse der Gleitkommaarithmetik einen Bereich und eine Genauigkeit aufweisen, die von der durch ihren Typ angegebenen abweichen, siehe FLT_EVAL_METHOD
Unäre Arithmetik
Die unären arithmetischen Operatorausdrücke haben die Form
+
Ausdruck
|
(1) | ||||||||
-
Ausdruck
|
(2) | ||||||||
| expression | - | Ausdruck eines beliebigen arithmetic type |
Sowohl unäres Plus als auch unäres Minus wenden zuerst integral promotions auf ihren Operanden an und dann
- Unärer Plus-Operator gibt den Wert nach Promotion zurück
- Unärer Minus-Operator gibt den negativen Wert nach Promotion zurück (außer dass das Negative eines NaN ein weiteres NaN ist)
Der Typ des Ausdrucks ist der Typ nach der Höherstufung, und die value category ist non-lvalue.
Hinweise
Der unäre Minus-Operator ruft undefiniertes Verhalten aufgrund von vorzeichenbehaftetem Ganzzahlüberlauf auf, wenn er auf INT_MIN , LONG_MIN oder LLONG_MIN auf typischen (2er-Komplement) Plattformen angewendet wird.
In C++ kann der unäre Operator
+
auch mit anderen eingebauten Typen wie Arrays und Funktionen verwendet werden, was in C nicht der Fall ist.
#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); // Definierte Ausführung, da Arithmetik für vorzeichenlose Ganzzahlen durchgeführt wird. // Daher ist die Berechnung (-1) modulo (2 hoch n) = UINT_MAX, wobei n // die Anzahl der Bits von unsigned int ist. Wenn unsigned int 32 Bit lang ist, dann ergibt dies // (-1) modulo (2 hoch 32) = 4294967295 printf("-1, where 1 is unsigned: %u\n", -1u); // Undefiniertes Verhalten, weil der mathematische Wert von -INT_MIN = INT_MAX + 1 // (d.h. 1 mehr als der maximal mögliche Wert für signed int) // // printf("%d\n", -INT_MIN); // Undefiniertes Verhalten, weil der mathematische Wert von -LONG_MIN = LONG_MAX + 1 // (d.h. 1 mehr als der maximal mögliche Wert für signed long) // // printf("%ld\n", -LONG_MIN); // Undefiniertes Verhalten, weil der mathematische Wert von -LLONG_MIN = LLONG_MAX + 1 // (d.h. 1 mehr als der maximal mögliche Wert für signed long long) // // printf("%lld\n", -LLONG_MIN); double complex z = 1 + 2*I; printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z)); }
Mögliche Ausgabe:
sizeof char: 1 sizeof int: 4 -1, where 1 is signed: -1 -1, where 1 is unsigned: 4294967295 -(1+2i) = -1.0-2.0
Additive Operatoren
Die binären additiven arithmetischen Operatorausdrücke haben die Form
lhs
+
rhs
|
(1) | ||||||||
lhs
-
rhs
|
(2) | ||||||||
-
- beide haben arithmetic types , einschließlich komplexer und imaginärer
- einer ist ein Zeiger auf einen vollständigen Objekttyp, der andere hat ganzzahligen Typ
-
- beide haben arithmetische Typen , inklusive komplexe und imaginäre
- lhs hat Zeiger auf vollständigen Objekttyp, rhs hat ganzzahligen Typ
- beide sind Zeiger auf vollständige Objekte mit kompatiblen Typen, unter Ignorierung von Qualifizierern
Arithmetische Addition und Subtraktion
Wenn beide Operanden arithmetische Typen haben, dann
- zuerst werden usual arithmetic conversions durchgeführt
- dann werden die Werte der Operanden nach den Konvertierungen gemäß den üblichen mathematischen Regeln addiert oder subtrahiert (bei Subtraktion wird rhs von lhs subtrahiert), mit der Ausnahme, dass
-
- wenn ein Operand NaN ist, ist das Ergebnis NaN
- Unendlich minus Unendlich ist NaN und FE_INVALID wird ausgelöst
- Unendlich plus negativ Unendlich ist NaN und FE_INVALID wird ausgelöst
Komplexe und imaginäre Addition und Subtraktion sind wie folgt definiert (beachten Sie, dass der Ergebnistyp imaginär ist, wenn beide Operanden imaginär sind, und komplex, wenn ein Operand reell und der andere imaginär ist, wie durch die üblichen arithmetischen Konvertierungen festgelegt):
| + oder - | 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
Zeigerarithmetik
-
Wenn der Zeiger
Pauf ein Element eines Arrays mit IndexIzeigt, dann
-
-
P
+
N
und
N
+
P
sind Zeiger, die auf ein Element desselben Arrays mit Index
I+Nzeigen -
P
-
N
ist ein Zeiger, der auf ein Element desselben Arrays mit Index
I-Nzeigt
-
P
+
N
und
N
+
P
sind Zeiger, die auf ein Element desselben Arrays mit Index
Das Verhalten ist nur definiert, wenn sowohl der ursprüngliche Zeiger als auch der resultierende Zeiger auf Elemente desselben Arrays oder auf das Element nach dem Ende des Arrays zeigen. Beachten Sie, dass die Ausführung von p-1, wenn p auf das erste Element eines Arrays zeigt, undefiniertes Verhalten ist und auf einigen Plattformen fehlschlagen kann.
-
Wenn der Zeiger
P1auf ein Element eines Arrays mit IndexI(oder eins hinter dem Ende) zeigt undP2auf ein Element desselben Arrays mit IndexJ(oder eins hinter dem Ende) zeigt, dann
-
- P1 - P2 hat den Wert gleich I - J und den Typ ptrdiff_t (was ein vorzeichenbehafteter Ganzzahltyp ist, typischerweise halb so groß wie die Größe des größten Objekts, das deklariert werden kann)
Das Verhalten ist nur definiert, wenn das Ergebnis in ptrdiff_t passt.
Für den Zweck der Zeigerarithmetik wird ein Zeiger auf ein Objekt, das kein Element eines Arrays ist, als Zeiger auf das erste Element eines Arrays der Größe 1 behandelt.
// 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]
Multiplikative Operatoren
Die binären multiplikativen arithmetischen Operatorausdrücke haben die Form
lhs
*
rhs
|
(1) | ||||||||
lhs
/
rhs
|
(2) | ||||||||
lhs
%
rhs
|
(3) | ||||||||
- Zunächst werden usual arithmetic conversions durchgeführt. Dann...
Multiplikation
Der binäre Operator * führt eine Multiplikation seiner Operanden durch (nach üblichen arithmetischen Konvertierungen) gemäß den üblichen arithmetischen Definitionen, außer dass
- wenn ein Operand ein NaN ist, ist das Ergebnis ein NaN
- Multiplikation von Unendlich mit Null ergibt NaN und FE_INVALID wird ausgelöst
- Multiplikation von Unendlich mit einem Wert ungleich Null ergibt Unendlich (selbst für komplexe Argumente)
Da in C jeder komplexe Wert mit mindestens einem unendlichen Anteil als Unendlichkeit gilt, selbst wenn sein anderer Anteil ein NaN ist, gelten die üblichen arithmetischen Regeln nicht für komplex-komplexe Multiplikation. Andere Kombinationen von Gleitkommaoperanden folgen der folgenden Tabelle:
| * | 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) | spezielle Regeln |
Neben der Behandlung von Unendlichkeiten ist es bei der komplexen Multiplikation nicht erlaubt, dass Zwischenergebnisse überlaufen, es sei denn,
#pragma STDC CX_LIMITED_RANGE
ist auf
ON
gesetzt, in welchem Fall der Wert berechnet werden darf, als ob durch
(x+iy)×(u+iv) = (xu-yv)+i(yu+xv)
, da der Programmierer die Verantwortung übernimmt, den Bereich der Operanden zu begrenzen und mit den Unendlichkeiten umzugehen.
Trotz des Verbots von unzulässigem Überlauf kann die komplexe Multiplikation unzugehörige Gleitkomma-Ausnahmen auslösen (ansonsten ist es unvertretbar schwierig, nicht-überlaufende Versionen zu implementieren)
#include <stdio.h> #include <stdio.h> #include <complex.h> #include <math.h> int main(void) { // TODO einfachere Fälle, einige aus C++ übernehmen double complex z = (1 + 0*I) * (INFINITY + I*INFINITY); // Lehrbuchformel würde ergeben // (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN // aber C liefert eine komplexe Unendlichkeit printf("%f + i*%f\n", creal(z), cimag(z)); // Lehrbuchformel würde ergeben // cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN // aber C liefert ±∞+i*nan double complex y = cexp(INFINITY + I*NAN); printf("%f + i*%f\n", creal(y), cimag(y)); }
Mögliche Ausgabe:
inf + i*inf inf + i*nan
Division
Der binäre Operator
/
dividiert den ersten Operanden durch den zweiten (nach üblichen arithmetischen Konvertierungen) gemäß den üblichen arithmetischen Definitionen, außer dass
- wenn der Typ nach den üblichen arithmetischen Konvertierungen ein Ganzzahltyp ist, ist das Ergebnis der algebraische Quotient (kein Bruch), gerundet in implementierungsdefinierter Richtung (bis C99) abgeschnitten in Richtung Null (seit C99)
- wenn ein Operand ein NaN ist, ist das Ergebnis ein NaN
-
wenn der erste Operand eine komplexe Unendlichkeit ist und der zweite Operand endlich ist, dann ist das Ergebnis des
/Operators eine komplexe Unendlichkeit -
wenn der erste Operand endlich ist und der zweite Operand eine komplexe Unendlichkeit ist, dann ist das Ergebnis des
/Operators eine Null.
Weil in C jeder komplexe Wert mit mindestens einem unendlichen Anteil als Unendlichkeit gilt, selbst wenn sein anderer Anteil ein NaN ist, gelten die üblichen arithmetischen Regeln nicht für die komplex-komplex Division. Andere Kombinationen von Fließkomma-Operanden folgen der folgenden Tabelle:
| / | 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) |
Neben der Behandlung von Unendlichkeiten ist es bei der komplexen Division nicht erlaubt, dass Zwischenergebnisse überlaufen, es sei denn,
#pragma STDC CX_LIMITED_RANGE
ist auf
ON
gesetzt, in welchem Fall der Wert berechnet werden darf, als ob durch
(x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u
2
+v
2
)
, da der Programmierer die Verantwortung übernimmt, den Bereich der Operanden zu begrenzen und mit den Unendlichkeiten umzugehen.
Trotz der Unterbindung unzulässigen Überlaufs kann die komplexe Division möglicherweise unbegründete Gleitkomma-Ausnahmen auslösen (ansonsten ist es unvertretbar schwierig, nicht-überlaufende Versionen zu implementieren)
Wenn der zweite Operand null ist, ist das Verhalten undefiniert, außer wenn IEEE-Gleitkommaarithmetik unterstützt wird und eine Gleitkommadivision stattfindet, dann
- Die Division einer Zahl ungleich Null durch ±0,0 ergibt das korrekt vorzeichenbehaftete Unendlich und FE_DIVBYZERO wird ausgelöst
- Die Division von 0,0 durch 0,0 ergibt NaN und FE_INVALID wird ausgelöst
Rest
Der binäre Operator % liefert den Rest der Division des ersten Operanden durch den zweiten (nach üblichen arithmetischen Umwandlungen).
Das Vorzeichen des Rests ist so definiert, dass, wenn der Quotient
a/b
im Ergebnistyp darstellbar ist, dann
(
a
/
b
)
*
b
+
a
%
b
==
a
.
Wenn der zweite Operand null ist, ist das Verhalten undefiniert.
Wenn der Quotient
a/b
im Ergebnistyp nicht darstellbar ist, ist das Verhalten von sowohl
a/b
als auch
a%b
undefiniert (das bedeutet
INT_MIN
%-
1
ist auf 2er-Komplement-Systemen undefiniert)
Hinweis: Der Restoperator funktioniert nicht mit Gleitkommatypen, die Bibliotheksfunktion fmod bietet diese Funktionalität.
Bitweise Logik
Die bitweisen arithmetischen Operatorausdrücke haben die Form
~
rhs
|
(1) | ||||||||
lhs
&
rhs
|
(2) | ||||||||
lhs
|
rhs
|
(3) | ||||||||
lhs
^
rhs
|
(4) | ||||||||
wobei
| lhs , rhs | - | Ausdrücke ganzzahligen Typs |
Zunächst führen die Operatoren & , ^ und | übliche arithmetische Konvertierungen auf beiden Operanden durch, und der Operator ~ führt Integer-Promotions auf seinem einzigen Operanden durch.
Dann werden die entsprechenden binären Logikoperatoren bitweise angewendet; das heißt, jedes Bit des Ergebnisses wird gesetzt oder gelöscht gemäß der Logikoperation (NOT, AND, OR oder XOR), angewendet auf die entsprechenden Bits der Operanden.
Hinweis: Bitweise Operatoren werden häufig zur Manipulation von Bit-Sets und Bit-Masken verwendet.
Hinweis: Für vorzeichenlose Typen (nach Höherstufung) ist der Ausdruck ~E äquivalent zum maximalen darstellbaren Wert des Ergebnistyps minus dem ursprünglichen Wert von E .
Mögliche Ausgabe:
Promoted mask: 0x000000f0 Value: 0x12345678 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x00000070
Shift-Operatoren
Die bitweisen Schiebeoperator-Ausdrücke haben die Form
lhs
<<
rhs
|
(1) | ||||||||
lhs
>>
rhs
|
(2) | ||||||||
wobei
| lhs , rhs | - | Ausdrücke ganzzahligen Typs |
Zunächst werden Integer-Promotions separat für jeden Operanden durchgeführt (Hinweis: Dies unterscheidet sich von anderen binären arithmetischen Operatoren, die alle übliche arithmetische Konvertierungen durchführen). Der Typ des Ergebnisses ist der Typ des lhs nach der Promotion.
Das Verhalten ist undefiniert, wenn rhs negativ ist oder größer oder gleich der Anzahl der Bits im promoted lhs ist.
Für vorzeichenlose
lhs
ist der Wert von
LHS << RHS
der Wert von
LHS * 2
RHS
, reduziert modulo Maximalwert des Rückgabetyps plus 1 (das heißt, bitweiser Linksshift wird durchgeführt und die Bits, die aus dem Zieltyp hinausgeschoben werden, werden verworfen). Für vorzeichenbehaftete
lhs
mit nichtnegativen Werten ist der Wert von
LHS << RHS
gleich
LHS * 2
RHS
, wenn er im höhergestuften Typ von
lhs
darstellbar ist, andernfalls ist das Verhalten undefiniert.
Für vorzeichenlose
lhs
und für vorzeichenbehaftete
lhs
mit nichtnegativen Werten ist der Wert von
LHS >> RHS
der ganzzahlige Teil von
LHS / 2
RHS
. Für negatives
LHS
ist der Wert von
LHS >> RHS
implementierungsdefiniert, wobei in den meisten Implementierungen dies einen arithmetischen Rechtsshift durchführt (sodass das Ergebnis negativ bleibt). Daher füllt in den meisten Implementierungen das Rechtsschieben eines vorzeichenbehafteten
LHS
die neuen höherwertigen Bits mit dem ursprünglichen Vorzeichenbit (d.h. mit 0, wenn es nicht-negativ war, und mit 1, wenn es negativ war).
#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" // Überlauf schneidet hohe Bits bei vorzeichenlosen Zahlen ab "0x10 << 10 = %#x\n", // char wird zu int heraufgestuft ulong_num << 1, ulong_num << 63, c << 10); long long long_num = -1000; printf("-1000 >> 1 = %lld\n", long_num >> ONE); // implementierungsdefiniert }
Mögliche Ausgabe:
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
Referenzen
- C17-Standard (ISO/IEC 9899:2018):
-
- 6.5.3.3 Unäre arithmetische Operatoren (S. 64)
-
- 6.5.5 Multiplikative Operatoren (S. 66)
-
- 6.5.6 Additive Operatoren (S. 66-68)
-
- 6.5.7 Bitweise Schiebeoperatoren (S. 68)
-
- 6.5.10 Bitweiser UND-Operator (S. 70)
-
- 6.5.11 Bitweiser exklusiver ODER-Operator (S. 70)
-
- 6.5.12 Bitweiser inklusiver ODER-Operator (S. 70-71)
- C11-Standard (ISO/IEC 9899:2011):
-
- 6.5.3.3 Unäre arithmetische Operatoren (S: 89)
-
- 6.5.5 Multiplikative Operatoren (S: 92)
-
- 6.5.6 Additive Operatoren (S: 92-94)
-
- 6.5.7 Bitweise Verschiebeoperatoren (S: 94-95)
-
- 6.5.10 Bitweiser UND-Operator (S: 97)
-
- 6.5.11 Bitweiser exklusiver ODER-Operator (S: 98)
-
- 6.5.12 Bitweiser inklusiver ODER-Operator (S: 98)
- C99-Standard (ISO/IEC 9899:1999):
-
- 6.5.3.3 Unäre arithmetische Operatoren (S. 79)
-
- 6.5.5 Multiplikative Operatoren (S. 82)
-
- 6.5.6 Additive Operatoren (S. 82-84)
-
- 6.5.7 Bitweise Verschiebeoperatoren (S. 84-85)
-
- 6.5.10 Bitweiser UND-Operator (S. 87)
-
- 6.5.11 Bitweiser exklusiver ODER-Operator (S. 88)
-
- 6.5.12 Bitweiser inklusiver ODER-Operator (S. 88)
- C89/C90 Standard (ISO/IEC 9899:1990):
-
- 3.3.3.3 Unäre arithmetische Operatoren
-
- 3.3.5 Multiplikative Operatoren
-
- 3.3.6 Additive Operatoren
-
- 3.3.7 Bitweise Verschiebeoperatoren
-
- 3.3.10 Bitweiser UND-Operator
-
- 3.3.11 Bitweiser exklusiver ODER-Operator
-
- 3.3.12 Bitweiser inklusiver ODER-Operator
Siehe auch
| Häufige Operatoren | ||||||
|---|---|---|---|---|---|---|
| Zuweisung |
Inkrement
Dekrement |
Arithmetisch | Logisch | Vergleich |
Member-
zugriff |
Sonstige |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
b
]
|
a
(
...
)
|
|
C++ Dokumentation
für
Arithmetische Operatoren
|