Implizite Typkonvertierungsregeln in C ++ - Operatoren

167

Ich möchte besser wissen, wann ich besetzen soll. Was sind die impliziten Typkonvertierungsregeln in C ++ beim Hinzufügen, Multiplizieren usw. Zum Beispiel:

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

und so weiter...

Wird der Ausdruck immer als der genauere Typ bewertet? Unterscheiden sich die Regeln für Java? Bitte korrigieren Sie mich, wenn ich diese Frage falsch formuliert habe.

Matt Montag
quelle
15
Denken Sie daran, ^ist XOR.
GManNickG
15
@int ^ float = Kompilierungsfehler :)
Serge Dundich

Antworten:

222

In C ++ wirken Operatoren (für POD-Typen) immer auf Objekte desselben Typs.
Wenn sie also nicht gleich sind, wird einer befördert, um mit dem anderen übereinzustimmen.
Der Typ des Ergebnisses der Operation ist der gleiche wie bei Operanden (nach der Konvertierung).

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

Hinweis. Die Mindestgröße der Operationen beträgt int. Also short/ charwerden befördert, intbevor die Operation abgeschlossen ist.

In all Ihren Ausdrücken intwird das zu a heraufgestuft, floatbevor die Operation ausgeführt wird. Das Ergebnis der Operation ist a float.

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>
Martin York
quelle
1
"Die Mindestgröße der Operationen ist int." - Das wäre sehr seltsam (was ist mit Architekturen, die char / short-Operationen effizient unterstützen?) Ist dies wirklich in der C ++ - Spezifikation enthalten?
Rafał Dowgird
3
@ Rafal: Ja. int soll der effizienteste Integer-Typ für den Betrieb auf einer bestimmten Plattform sein. char muss immer 1 sein, aber short kann die gleiche Größe wie int haben.
Martin York
1
@ Rafał: ja, es ist sehr seltsam und es ist im Standard. In vielen Fällen könnte die von Ihnen beschriebene Architektur ihren supereffizienten charTyp verwenden. Wenn der Wert von a char + charzugewiesen ist char, kann er einfach die Arithmetik in ausführen charund beispielsweise umbrechen. Wenn das Ergebnis jedoch zugewiesen intist, muss es die Arithmetik in einem Typ ausführen, der groß genug ist, um das richtige Ergebnis zu erhalten, wenn es größer als ist CHAR_MAX.
Steve Jessop
2
Ich möchte nur die Tatsache betonen, dass int zu unsigned int befördert wird !!! Ich habe seit einigen Tagen mit Bugs zu kämpfen , weil ich hatte den Eindruck, dass beide würden gefördert zu int oder lang , so dass ein mögliches negatives Ergebnis nicht eine Unterschreitung / wrap-around führen würde.
Nitsas
10
Beispiel für das Problem " int wird zu vorzeichenlosem int befördert ": ((int) 4) - ((unsigned int) 5)führt 4294967295zu 32-Bit-Ints und 32-Bit-Ints ohne Vorzeichen.
Nitsas
33

Arithmetische Operationen mit floatErgebnissen in float.

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

Für detailliertere Antwort. Schauen Sie sich an, was der Abschnitt §5 / 9 des C ++ - Standards sagt

Viele binäre Operatoren, die Operanden vom Typ Arithmetik oder Aufzählung erwarten, verursachen Konvertierungen und ergeben auf ähnliche Weise Ergebnistypen. Der Zweck besteht darin, einen gemeinsamen Typ zu erhalten, der auch der Typ des Ergebnisses ist .

Dieses Muster wird als übliche arithmetische Konvertierung bezeichnet, die wie folgt definiert ist:

- Wenn einer der Operanden vom Typ long double ist, wird der andere in long double konvertiert.

- Wenn einer der Operanden doppelt ist, wird der andere in double konvertiert.

- Wenn andernfalls einer der Operanden float ist, wird der andere in float konvertiert.

- Andernfalls werden die integralen Beförderungen (4.5) für beide Operanden durchgeführt.54)

- Wenn einer der Operanden lange ohne Vorzeichen ist, wird der andere in Long ohne Vorzeichen konvertiert.

- Wenn andernfalls ein Operand ein Long-Int und der andere ein Int ohne Vorzeichen ist, muss das Int ohne Vorzeichen in ein Long-Int konvertiert werden, wenn ein Long-Int alle Werte eines Int ohne Vorzeichen darstellen kann. Andernfalls werden beide Operanden in unsigned long int konvertiert.

- Wenn andernfalls einer der Operanden lang ist, wird der andere in long konvertiert.

- Andernfalls wird, wenn einer der Operanden ohne Vorzeichen ist, der andere in einen Vorzeichen ohne Vorzeichen konvertiert.

[Hinweis: Andernfalls bleibt nur der Fall, dass beide Operanden int sind.]

Nawaz
quelle
3
... solange der andere Typ weder doublenoch ist long double.
CB Bailey
1
@ Charles: Richtig. Ich habe den relevanten Abschnitt aus dem Standard zitiert, um ihn weiter zu verdeutlichen.
Nawaz
Kann eine Ganzzahl also immer ohne Datenverlust in Float konvertiert werden? (zB indem man den Exponenten auf Null setzt und alles für die Mantisse verwendet)?
Marco A.
1
Diese Antwort ist veraltet. Update vorschlagen. Insbesondere long longund hier unsigned longnicht angesprochen.
chux
@ MarcoA. Ein 32-Bit floathat nicht genügend Bits in der Mantisse (24 Bit für IEEE-754 ) für ein 32-Bit int, daher kann es zu Datenverlusten kommen. Ein 64-Bit doublesollte in Ordnung sein.
Mark Ransom
17

Da die anderen Antworten nicht über die Regeln in C ++ 11 sprechen, ist hier eine. Aus dem C ++ 11-Standard (Entwurf n3337) §5 / 9 (betonte den Unterschied):

Dieses Muster wird als übliche arithmetische Konvertierung bezeichnet , die wie folgt definiert ist:

- Wenn einer der Operanden vom Typ Aufzählung mit Gültigkeitsbereich ist, werden keine Konvertierungen durchgeführt. Wenn der andere Operand nicht denselben Typ hat, ist der Ausdruck falsch geformt.

- Wenn einer der Operanden vom Typ long double ist, wird der andere in long double konvertiert.

- Wenn einer der Operanden doppelt ist, wird der andere in double konvertiert.

- Wenn andernfalls einer der Operanden float ist, wird der andere in float konvertiert.

- Andernfalls werden die integralen Beförderungen für beide Operanden durchgeführt. Dann gelten die folgenden Regeln für die beförderten Operanden:

- Wenn beide Operanden den gleichen Typ haben, ist keine weitere Konvertierung erforderlich.

- Wenn andernfalls beide Operanden Ganzzahltypen mit Vorzeichen oder beide Ganzzahltypen ohne Vorzeichen haben, wird der Operand mit dem Typ des Konvertierungsrangs für eine geringere Ganzzahl in den Typ des Operanden mit dem höheren Rang konvertiert.

- Wenn andernfalls der Operand mit vorzeichenlosem Integer-Typ einen Rang größer oder gleich dem Rang des Typs des anderen Operanden hat, wird der Operand mit vorzeichenbehaftetem Integer-Typ in den Typ des Operanden mit vorzeichenlosem Integer-Typ konvertiert.

- Wenn andernfalls der Typ des Operanden mit vorzeichenbehaftetem Integer-Typ alle Werte des Typs des Operanden mit vorzeichenlosem Integer-Typ darstellen kann, wird der Operand mit vorzeichenlosem Integer-Typ in den Typ des Operanden mit vorzeichenbehaftetem Integer-Typ konvertiert.

- Andernfalls werden beide Operanden in den vorzeichenlosen Integer-Typ konvertiert, der dem Typ des Operanden mit vorzeichenbehaftetem Integer-Typ entspricht.

Siehe hier für eine Liste , die häufig aktualisiert wird .

legends2k
quelle
1
Diese Regeln waren in allen Versionen von C ++ gleich, mit Ausnahme von Aufzählungen mit Gültigkeitsbereich, die natürlich in C ++ 11 hinzugefügt wurden
MM
6

Diese Antwort richtet sich größtenteils an einen Kommentar von @ RafałDowgird:

"Die Mindestgröße der Operationen ist int." - Das wäre sehr seltsam (was ist mit Architekturen, die char / short-Operationen effizient unterstützen?) Ist dies wirklich in der C ++ - Spezifikation enthalten?

Beachten Sie, dass der C ++ - Standard die alles entscheidende "Als-ob" -Regel enthält. Siehe Abschnitt 1.8: Programmausführung:

3) Diese Bestimmung wird manchmal als "Als-ob" -Regel bezeichnet, da es einer Implementierung freigestellt ist, jede Anforderung des Standards zu ignorieren, solange das Ergebnis so ist, als ob die Anforderung eingehalten worden wäre, soweit sich aus dem Beobachtbaren ergibt Verhalten des Programms.

Der Compiler kann eine intGröße von 8 Bit nicht festlegen , selbst wenn sie die schnellste wäre, da der Standard ein Minimum von 16 Bit vorschreibt int.

Daher könnte im Fall eines theoretischen Computers mit superschnellen 8-Bit-Operationen die implizite Heraufstufung zur intArithmetik von Bedeutung sein. Bei vielen Operationen können Sie jedoch nicht feststellen, ob der Compiler die Operationen tatsächlich mit der Genauigkeit von a ausgeführt intund dann in a konvertiert hat char, um sie in Ihrer Variablen zu speichern, oder ob die Operationen die ganze Zeit in char ausgeführt wurden.

Stellen Sie sich zum Beispiel vor, unsigned char = unsigned char + unsigned char + unsigned charwo die Addition überlaufen würde (nehmen wir jeweils einen Wert von 200 an). Wenn Sie zu befördert intwürden, würden Sie 600 erhalten, die dann implizit in ein unsigned charModulo 256 umgewandelt würden, was ein Endergebnis von 88 ergeben würde. Wenn Sie keine solchen Beförderungen durchführen würden, müssten Sie zwischen den ersten wechseln zwei Ergänzungen, die das Problem von 200 + 200 + 200auf reduzieren würden, nämlich 144 + 200344, was sich auf 88 reduziert. Mit anderen Worten, das Programm kennt den Unterschied nicht, so dass der Compiler das Mandat zur Ausführung von Zwischenoperationen ignorieren kann, intwenn die Operanden haben ein niedrigerer Rang als int.

Dies gilt im Allgemeinen für Addition, Subtraktion und Multiplikation. Dies gilt im Allgemeinen nicht für Division oder Modul.

David Stone
quelle
4

Wenn Sie die vorzeichenlosen Typen ausschließen, gibt es eine geordnete Hierarchie: signiertes Zeichen, kurz, int, lang, lang, lang, float, doppelt, lang, doppelt. Zunächst wird alles, was oben vor int steht, in int konvertiert. Dann wird in einer binären Operation der Typ mit dem niedrigeren Rang in den Typ mit dem höheren Rang konvertiert, und die Ergebnisse sind der Typ mit dem höheren Rang. (Sie werden feststellen, dass aus der Hierarchie jedes Mal, wenn ein Gleitkomma- und ein Integraltyp beteiligt sind, der Integraltyp in den Gleitkommatyp konvertiert wird.)

Unsigned verkompliziert die Dinge ein wenig: Es stört das Ranking und Teile des Rankings werden implementiert. Aus diesem Grund ist es am besten, signierte und nicht signierte nicht im selben Ausdruck zu mischen. (Die meisten C ++ - Experten scheinen vorzeichenlose Vorgänge zu vermeiden, es sei denn, es handelt sich um bitweise Operationen. Dies wird zumindest von Stroustrup empfohlen.)

James Kanze
quelle
3
Stroustrup kann empfehlen, was ihm gefällt, aber die Verwendung eines Signierbaren intfür eine Zahl, die niemals negativ sein muss, ist eine völlige Verschwendung von vollen 50% des verfügbaren Bereichs. Ich bin sicherlich kein Stroustrup, aber ich benutze unsignedstandardmäßig und signednur, wenn ich einen Grund habe.
underscore_d
1
Das ist alles schön und gut, underscore_d, bis zu dem Tag, an dem Sie subtrahieren müssen. Das Hauptproblem bei vorzeichenlosen Zahlen in C ++ besteht darin, dass sie bei der Subtraktion ohne Vorzeichen bleiben. Angenommen, Sie schreiben eine Funktion, um festzustellen, ob ein std :: -Vektor in Ordnung ist. Sie könnten schreiben bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;und dann wären Sie verärgert, wenn Sie feststellen würden, dass es für leere Vektoren abstürzt, weil size () - 1 18446744073709551615 zurückgibt.
jorgbrown
3

Meine Lösung zum Problem bekam WA (falsche Antwort), dann änderte es einen intzu long long intund es gab AC (nehmen) . Zuvor habe ich versucht long long int += int * int, und nachdem ich es korrigiert habe long long int += long long int * int. Googeln habe ich mir ausgedacht,

1. Arithmetische Umrechnungen

Bedingungen für die Typkonvertierung:

Bedingungen erfüllt ---> Konvertierung

  • Jeder Operand ist vom Typ long double . ---> Ein anderer Operand wird in den Typ long double konvertiert .

  • Die vorhergehende Bedingung ist nicht erfüllt und jeder Operand ist vom Typ double . ---> Ein anderer Operand wird in den Typ double konvertiert .

  • Vorherige Bedingungen sind nicht erfüllt und jeder Operand ist vom Typ float . ---> Ein anderer Operand wird in den Typ float konvertiert .

  • Vorherige Bedingungen nicht erfüllt (keiner der Operanden ist vom schwebenden Typ). ---> Integrale Promotions werden für die Operanden wie folgt durchgeführt:

    • Wenn einer der Operanden vom Typ unsigned long ist , wird der andere Operand in den Typ unsigned long konvertiert .
    • Wenn die vorhergehende Bedingung nicht erfüllt ist und einer der Operanden vom Typ long und der andere vom Typ unsigned int ist , werden beide Operanden in den Typ unsigned long konvertiert .
    • Wenn die beiden vorhergehenden Bedingungen nicht erfüllt sind und einer der Operanden vom Typ long ist , wird der andere Operand in den Typ long konvertiert .
    • Wenn die vorhergehenden drei Bedingungen nicht erfüllt sind und einer der Operanden vom Typ unsigned int ist , wird der andere Operand in den Typ unsigned int konvertiert .
    • Wenn keine der vorhergehenden Bedingungen erfüllt ist, werden beide Operanden in den Typ int konvertiert .

2. Ganzzahlige Konvertierungsregeln

  • Ganzzahlige Aktionen:

Ganzzahlige Typen, die kleiner als int sind, werden heraufgestuft, wenn eine Operation an ihnen ausgeführt wird. Wenn alle Werte des ursprünglichen Typs als int dargestellt werden können, wird der Wert des kleineren Typs in ein int konvertiert. Andernfalls wird es in ein vorzeichenloses int konvertiert. Ganzzahlige Heraufstufungen werden als Teil der üblichen arithmetischen Konvertierungen auf bestimmte Argumentausdrücke angewendet. Operanden der unären Operatoren +, - und ~; und Operanden der Schichtoperatoren.

  • Integer Conversion Rank:

    • Keine zwei vorzeichenbehafteten Ganzzahltypen dürfen denselben Rang haben, selbst wenn sie dieselbe Darstellung haben.
    • Der Rang eines vorzeichenbehafteten Integer-Typs muss größer sein als der Rang eines vorzeichenbehafteten Integer-Typs mit geringerer Genauigkeit.
    • Der Rang von long long intist größer als der Rang von long int, der größer sein soll als der Rang von int, der größer sein soll als der Rang von short int, der größer sein soll als der Rang von signed char.
    • Der Rang eines vorzeichenlosen Integer-Typs entspricht gegebenenfalls dem Rang des entsprechenden vorzeichenbehafteten Integer-Typs.
    • Der Rang eines Standard-Integer-Typs muss größer sein als der Rang eines erweiterten Integer-Typs mit derselben Breite.
    • Der Rang von charsoll dem Rang von signed charund entsprechen unsigned char.
    • Der Rang eines erweiterten vorzeichenbehafteten Integer-Typs relativ zu einem anderen erweiterten vorzeichenbehafteten Integer-Typ mit derselben Genauigkeit ist implementierungsdefiniert, unterliegt jedoch weiterhin den anderen Regeln zur Bestimmung des Integer-Konvertierungsrangs.
    • Wenn für alle ganzzahligen Typen T1, T2 und T3 T1 einen höheren Rang als T2 und T2 einen höheren Rang als T3 hat, hat T1 einen höheren Rang als T3.
  • Übliche arithmetische Umrechnungen:

    • Wenn beide Operanden denselben Typ haben, ist keine weitere Konvertierung erforderlich.
    • Wenn beide Operanden vom gleichen ganzzahligen Typ (vorzeichenbehaftet oder vorzeichenlos) sind, wird der Operand mit dem Typ des Konvertierungsrangs für kleinere Ganzzahlen in den Typ des Operanden mit höherem Rang konvertiert.
    • Wenn der Operand mit vorzeichenlosem Integer-Typ einen Rang hat, der größer oder gleich dem Rang des Typs des anderen Operanden ist, wird der Operand mit vorzeichenbehaftetem Integer-Typ in den Typ des Operanden mit vorzeichenlosem Integer-Typ konvertiert.
    • Wenn der Typ des Operanden mit vorzeichenbehaftetem Integer-Typ alle Werte des Typs des Operanden mit vorzeichenlosem Integer-Typ darstellen kann, wird der Operand mit vorzeichenlosem Integer-Typ in den Typ des Operanden mit vorzeichenbehaftetem Integer-Typ konvertiert.
    • Andernfalls werden beide Operanden in den vorzeichenlosen Integer-Typ konvertiert, der dem Typ des Operanden mit vorzeichenbehaftetem Integer-Typ entspricht. Bestimmte Operationen können die Semantik der üblichen arithmetischen Operationen ergänzen oder ändern.
garakchy
quelle
1

In Kapitel 4 geht es um Conversions, aber ich denke, Sie sollten sich hauptsächlich für Folgendes interessieren:

4.5 Integrale Promotions [conv.prom]
Ein Wert vom Typ char, ein vorzeichenbehaftetes Zeichen, ein vorzeichenloses Zeichen, ein kurzes int oder ein vorzeichenloses kurzes int kann in einen r-Wert vom Typ int konvertiert werden, wenn int alle Werte des Quelltyps darstellen kann. Andernfalls
kann der Quell-r-Wert in einen r-Wert vom Typ unsigned int konvertiert werden.
Ein r-Wert vom Typ wchar_t (3.9.1) oder ein Aufzählungstyp (7.2) kann in einen r-Wert des ersten
der folgenden Typen konvertiert werden, der alle Werte des zugrunde liegenden Typs darstellen kann: int, unsigned int,
long oder unsigned lange.
Ein r-Wert für ein integrales Bitfeld (9.6) kann in einen r-Wert vom Typ int konvertiert werden, wenn int alle
Werte des Bitfelds darstellen kann . Andernfalls kann es in unsigned int konvertiert werden, wenn unsigned int wiedergeben kann
Senden Sie alle Werte des Bitfelds erneut. Wenn das Bitfeld noch größer ist, gilt keine integrale Heraufstufung. Wenn das
Bitfeld einen Aufzählungstyp hat, wird es für Heraufstufungszwecke wie jeder andere Wert dieses Typs behandelt.
Ein Wert vom Typ bool kann in einen Wert vom Typ int konvertiert werden, wobei false zu Null und true
zu Eins wird.
Diese Conversions werden als integrale Aktionen bezeichnet.

4.6 Gleitkomma- Heraufstufung [conv.fpprom]
Ein Wert vom Typ float kann in einen Wert vom Typ double konvertiert werden. Der Wert bleibt unverändert.
Diese Konvertierung wird als Gleitkomma-Promotion bezeichnet.

Daher sind alle Konvertierungen mit float - das Ergebnis ist float.

Nur derjenige, an dem beide int beteiligt sind - das Ergebnis ist int: int / int = int

BЈовић
quelle
1

Der Typ des Ausdrucks wird in den größten von beiden konvertiert, wenn nicht beide Teile vom gleichen Typ sind . Das Problem hierbei ist zu verstehen, welches größer als das andere ist (es hat nichts mit der Größe in Bytes zu tun).

In Ausdrücken, an denen eine reelle Zahl und eine Ganzzahl beteiligt sind, wird die Ganzzahl zur reellen Zahl heraufgestuft. In int + float ist der Typ des Ausdrucks beispielsweise float.

Der andere Unterschied hängt mit der Fähigkeit des Typs zusammen. Beispielsweise ergibt sich ein Ausdruck mit einem int und einem long int vom Typ long int.

Baltasarq
quelle
2
Das ist nicht wahr. Auf Mai-Plattformen longist a "größer" als a, floataber was ist die Art von long+ float?
CB Bailey
1
-1: Was meinst du mit am größten ? Ist ein Float größer als ein Int? Oder umgekehrt ?
Paul R
2
Vielen Dank für Ihre Kommentare. Ja, die Größe in Bytes ist hier überhaupt nicht von Interesse. Es reicht offensichtlich nicht aus, die größte Kursivschrift zu verwenden, um die Antwort zu erklären. Auf jeden Fall macht es keinen Sinn, es genauer zu erklären, da es jetzt andere, sehr gründliche Antworten gibt.
Baltasarq
-2

Vorbehalt!

Die Konvertierungen erfolgen von links nach rechts.

Versuche dies:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0
Habib
quelle
8
Dies liegt nicht an der Konvertierung, sondern an der Priorität des Operators. j + i * kwürde in 101 führen.
gartenriese