Arithmetische Umwandlung

Unter arithmetischer Umwandlung versteht man einen eingebauten Mechanismus in C und C++, der zwei Werte mit unterschiedlichem arithmetischem Typ (Integer- oder Fliesskomma-Typen) so umwandelt, dass sie danach denselben Typ und möglichst den ursprünglichen Wert haben. Dieser Mechanismus wird für Operatoren verwendet, welche zwei Operanden erwarten.

Details

Operatoren erzeugen grundsätzlich je nach Typ der verknüpften Operanden unterschiedlichen Assembler-Code. Wenn ein Operator zwei Operanden erwartet, muss somit theoretisch für jede Typ-Kombination eine entsprechende Umsetzung in Assembler existieren. Gerade jedoch für Operatoren, welche arithmetische Typen wie Integer- oder Fliesskomma-Typen erwarten, existieren in Assembler beinahe ausschliesslich nur Befehle, welche Werte mit gleichem Typ miteinander verknüpfen können. Die arithmetische Umwandlung von C und C++ sorgt somit dafür, dass Werte unterschiedlichen Typs in einen bestmöglichen, gleichen Typ umgewandelt werden, worauf der Operator den entsprechenden Assemblerbefehl erzeugen kann.

Die arithmetische Umwandlung behandelt somit explizit die Wahl eines bestmöglichen Typs, um (ausschliesslich) Operatoren, welche zwei Operanden erwarten, eine Verknüpfung zu ermöglichen.

Die Wahl des Umwandlungstypen

Jeder arithmetische Typ besitzt eine sogenannte Mächtigkeit, wobei long double der mächtigste Typ ist und int der schwächste. Als Faustregel gilt bei der arithmetischen Umwandlung: Der schwächere wird in den mächtigeren umgewandelt.

Die Art der Umwandlung wird in den Compilern durch die Abarbeitung von Regeln festgelegt, wobei die erste zutreffende Regel die bestimmende ist. Sie sind in dem nachfolgenden Text der Reihe nach von oben nach unten aufgelistet. Die erste Regel lautet:

Die Fliesskommatypen long double, double und float sind mächtiger als alle Integer-Typen und long double ist der mächtigste von allen. Die Regeln für Fliesskomma-Typen sind die folgenden:

Ist keiner der Operanden ein Fliesskomma-Typ, so treten die Regeln der Integer-Typen in Kraft. Leider sind Integer-Typen historisch bedingt nicht immer konsistent, weswegen sich verschiedene Quellen von verschiedenen Compilern in diesem Punkt unterscheiden. Es ist somit nicht möglich, einheitliche Regeln anzugeben, allerdings bietet der Autor folgende einfache Regeln als Orientierung an, welche in heutigen Compilern zumindest in den nicht-extremen Fällen stimmen sollten:

Der Grund für die nicht-konsistenten Regeln der Umwandlungen von Integer-Typen liegt in der Tatsache, dass ein int früher je nach Prozessor mit 2 oder 4 Bytes spezifiziert war. Die 2-Byte-Variante wurde manchmal mit short int oder int bezeichnet, die 4-Byte-Variante mit int oder long int. Die heute gängigen C-Standards spezifizieren 4 Bytes für einen int. Es wird vermutet, dass heute immer noch verschiedene Standards in Benutzung sind, welche unterschiedliche Mechanismen für die Umwandlung verwenden. Doch mit den oben genannten Regeln kommt man (vielleicht abgesehen von gewissen Grenzfällen) sehr gut über die Runden. Im Zweifelsfalle empfielt es sich, explizite Casts zu verwenden.

An diesem Punkt ist anzumerken, dass stets zur Compilezeit bestimmt wird, in welchen Typ umgewandelt wird. Diese Bemerkung ist hautpsächlich wichtig für die in anderen Quellen beschriebenen Umwandlungen bei Grenzfällen.

Die Typen short und char sowie w_char werden, falls die Typen der beiden Operanden nicht übereinstimmen, allesamt in einen int umgewandelt. Der Grund hierfür ist, dass int die kleinste Recheneinheit eines Prozessors ist. Falls die Typen der beiden Operanden übereinstimmen, existieren jedoch in vielen Prozessoren die dazu passenden Assembler-Befehle.

Weiteres

Die tatsächliche Umwandlung der Typen ineinander kann bei den entsprechenden Typen nachgelesen werden.

Der durch die oben genannten Regeln gefundene Umwandlungstyp deklariert bei vielen Operatoren gleichzeitig den Typ des Rückgabewertes des Operators, was bei den detailierten Beschreibungen der Operatoren jeweils angemerkt wird.

Die arithmetische Umwandlung erfolgt grundsätzlich automatisch, wann immer der Compiler dies als notwendig erachtet. In den allermeisten Fällen verläuft die automatische Umwandlung der Werte problemlos, allerdings gibt es Fälle, bei denen der Programmierer eingreifen muss. Umwandlungen können somit durch die Angabe von Casts auch explizit erzwungen werden.

Das Problem einer solchen Umwandlung ist, dass verschiedene Typen generell nicht verlustfrei ineinander umgewandelt werden können. Beispielsweise ist es nicht möglich, eine Zahl mit Nachkommastellen (Fliesskommazahl) durch eine Zahl ohne Nachkommastellen (Integer) darzustellen. Es ist auch nicht möglich, eine negative Zahl mittels eines vorzeichenlosen Typs zu speichern. Und eine Zahl, die den Werteumfang eines Typs sprengt, kann ebenfalls nicht korrekt umgewandelt werden.

Eine denkbare Lösung wäre, alle Werte in einen möglichst mächtigen Typ umzuwandeln, mit dem möglichst alle Werte dargestellt werden können, wie beispielsweise long double. Allerdings würde dies sowohl den Platzverbrauch, als auch die Laufzeit eines jeden Programmes um ein Vielfaches in die Höhe treiben und ausserdem sind Fliesskommazahlen für viele Informatikprobleme ungeeignet.

Bei der Verwendung von festen Werten mit arithmetischen Operatoren empfielt es sich aus verschiedenen Gründen, darauf zu achten, dass keine implizite arithmetische Umwandlungen auftreten. Um beispielsweise die Zahl 1 als Fliesskommazahl festzulegen, sollte man 1. schreiben.

Man beachte, dass arithmetische Umwandlungen nicht gratis sind, sondern eine gewisse Anzahl an Assembleranweisungen kosten. Insbesondere Konvertierungen von Fliesskommazahlen erfordern komplexe Befehle auf Fliesskomma-Registern. Für zeitkritische Anwendungen lohnt es sich daher, auf diese Umwandlungen zu achten.