C++-Programmierung/ Einführung in C++/ Rechnen mit unterschiedlichen Datentypen
Aus Wikibooks
Sie kennen nun die Datentypen in C++ und haben auch schon mit int-Variablen gerechnet. In diesem Kapitel erfahren Sie, wie man mit Variablen unterschiedlichen Typs rechnet. Es geht also weniger um das Ergebnis selbst, als viel mehr darum wie der Ergebnisdatentyp lautet.
Inhaltsverzeichnis |
[Bearbeiten] Ganzzahlen unter sich
Das Rechnen mit Ganzzahlen ist leicht zu begreifen. Die „kleinen“ Datentypen werden als int behandelt. Bei den größeren entscheidet der größte Datentyp über den Ergebnistyp. Die folgende Liste zeigt die Zusammenhänge:
char + char => int | wchar_t + char => int char + wchar_t => int | wchar_t + wchar_t => int char + signed char => int | wchar_t + signed char => int char + unsigned char => int | wchar_t + unsigned char => int char + short => int | wchar_t + short => int char + unsigned short => int | wchar_t + unsigned short => int char + int => int | wchar_t + int => int char + unsigned int => unsigned int | wchar_t + unsigned int => unsigned int char + long => long | wchar_t + long => long char + unsigned long => unsigned long | wchar_t + unsigned long => unsigned long signed char + char => int | unsigned char + char => int signed char + wchar_t => int | unsigned char + wchar_t => int signed char + signed char => int | unsigned char + signed char => int signed char + unsigned char => int | unsigned char + unsigned char => int signed char + short => int | unsigned char + short => int signed char + unsigned short => int | unsigned char + unsigned short => int signed char + int => int | unsigned char + int => int signed char + unsigned int => unsigned int | unsigned char + unsigned int => unsigned int signed char + long => long | unsigned char + long => long signed char + unsigned long => unsigned long | unsigned char + unsigned long => unsigned long short + char => int | unsigned short + char => int short + wchar_t => int | unsigned short + wchar_t => int short + signed char => int | unsigned short + signed char => int short + unsigned char => int | unsigned short + unsigned char => int short + short => int | unsigned short + short => int short + unsigned short => int | unsigned short + unsigned short => int short + int => int | unsigned short + int => int short + unsigned int => unsigned int | unsigned short + unsigned int => unsigned int short + long => long | unsigned short + long => long short + unsigned long => unsigned long | unsigned short + unsigned long => unsigned long int + char => int | unsigned int + char => unsigned int int + wchar_t => int | unsigned int + wchar_t => unsigned int int + signed char => int | unsigned int + signed char => unsigned int int + unsigned char => int | unsigned int + unsigned char => unsigned int int + short => int | unsigned int + short => unsigned int int + unsigned short => int | unsigned int + unsigned short => unsigned int int + int => int | unsigned int + int => unsigned int int + unsigned int => unsigned int | unsigned int + unsigned int => unsigned int int + long => long | unsigned int + long => long oder unsigned long int + unsigned long => unsigned long | unsigned int + unsigned long => unsigned long long + char => long | unsigned long + char => unsigned long long + wchar_t => long | unsigned long + wchar_t => unsigned long long + signed char => long | unsigned long + signed char => unsigned long long + unsigned char => long | unsigned long + unsigned char => unsigned long long + short => long | unsigned long + short => unsigned long long + unsigned short => long | unsigned long + unsigned short => unsigned long long + int => long | unsigned long + int => unsigned long long + unsigned int => unsigned long | unsigned long + unsigned int => unsigned long long + long => long | unsigned long + long => unsigned long long + unsigned long => unsigned long | unsigned long + unsigned long => unsigned long
Zugegebenermaßen wirkt dies erst einmal erschlagend, aber es ist eigentlich nicht schwer zu begreifen. Bei jeder Rechenoperation hat jeder der 2 Operanden, sowie das Ergebnis der Rechnung einen Datentyp:
int main(){
char Zahl1=22;
short Zahl2=40;
std::cout << Zahl1 * Zahl2 << std::endl; // 22 * 40 = 880
// char + short => int
}
[Bearbeiten] Gleitkommarechnen
Beim Rechnen mit Gleitkommazahlen gelten im Grunde die gleichen Regeln wie bei Ganzzahlen. Der Ergbnistyp entspricht auch hier dem des Operanden mit dem „größeren“ Typ. Die Reihenfolge lautet: float, double, long double. Es gilt also:
float + float => float float + double => double float + long double => long double double + float => double double + double => double double + long double => long double long double + float => long double long double + double => long double long double + long double => long double
[Bearbeiten] Casting
Casting meint in diesem Zusammenhang, die Umwandlung eines Datentyps in einen anderen. Diese Typumwandlung kann sowohl automatisch (implizit) stattfinden, als auch vom Programmierer angegeben (explizit) werden.
[Bearbeiten] Implizite Typumwandlung
Mit impliziter Typumwandlung hatten Sie bereits reichlich zu tun, denn es kann ausschließlich mit Zahlen gerechnet werden die den gleichen Typ besitzen.
Beispiele:
char + int => int | int + int => int short + unsigned int => unsigned int | unsigned int + unsigned int => unsigned int float + double => double | double + double => double
[Bearbeiten] Umformungsregeln
Viele binäre Operatoren, die arithmetische oder Aufzählungsoperanden erwarten, verursachen Umwandlungen und ergeben Ergebnistypen auf ähnliche Weise. Der Zweck ist, einen gemeinsamen Typ zu finden, der auch der Ergebnistyp ist. Dieses Muster wird "die üblichen arithmetischen Umwandlungen" genannt, die folgendermaßen definiert sind:
- Wenn ein Operand vom Typ long double ist, dann wird der andere zu long double konvertiert.
- Andernfalls, wenn ein Operand vom Typ double ist, dann wird der andere zu double konvertiert.
- Andernfalls, wenn ein Operand vom Typ float ist, dann wird der andere zu float konvertiert.
- Ansonsten werden die integralen Umwandlungen auf beide Operanden angewendet:
- Wenn ein Operand vom Typ unsigned long ist, dann wird der andere zu unsigned long konvertiert.
- Andernfalls, wenn ein Operand vom Typ long und der andere vom Typ unsigned int, dann wird, falls ein long alle Werte eines unsigned int darstellen kann, der unsigned int-Operand zu long konvertiert; andernfalls werden beide Operanden zu unsigned long konvertiert.
- Andernfalls, wenn ein Operand vom Typ long ist, dann wird der andere zu long konvertiert.
- Andernfalls, wenn ein Operand vom Typ unsigned int ist, dann wird der andere zu unsigned int konvertiert.
Hinweis: Der einzig verbleibende Fall ist, dass beide Operanden vom Typ int sind.
Diese Regeln wurden so aufgestellt, dass dabei stets ein Datentyp in einen anderen Datentyp mit "größerem" Wertebereich umgewandelt wird. Das stellt sicher, dass bei der Typumwandlung keine Wertverluste durch Überläufe entstehen. Es können allerdings bei der Umwandlung von Ganzzahlen in float-Werte Rundungsfehler auftreten:
Diese Programmzeile gibt auf einem Computer, wo float eine 32-Bit-Gleitkommazahl ist, folgendes aus:
- 17000000 + 1.0f = 17000000.000000.
Warum? Für die Berechnung werden beide Operanden in den Datentyp float konvertiert und anschließend addiert. Eine 32-Bit-Gleitkommazahl ist aber nicht in der Lage, Zahlen in der Größenordnung von 17 Mio. mit der nötigen Genauigkeit zu speichern, um zwischen 17000000 und 17000001 zu unterscheiden. Das Ergebnis der Addition wird daher wieder auf 17000000 gerundet. Dies geschieht in diesem Falle sogar, obwohl der Wert anschließend auf double "aufgeweitet" wird, da die printf-Methode Gleitkommawerte standardmäßig als double erhält (und durch die Formatangabe "%f" auch als double erwartet).
[Bearbeiten] Explizite Typumwandlung
In C++ gibt es dafür zwei Möglichkeiten. Zum einen den aus C übernommenen Cast (Typ)Wert und zum anderen die vier (neuen) C++ Casts.
const_cast< Zieltyp >(Variable)
dynamic_cast< Zieltyp >(Variable)
reinterpret_cast< Zieltyp >(Variable)
[Bearbeiten] Ganzzahlen und Gleitkommazahlen
Wird mit einer Ganzzahl und einer Gleitkommazahl gerechnet, so ist das Ergebnis vom gleichen Typ wie die Gleitkommazahl.
[Bearbeiten] Rechnen mit Zeichen
Mit Zeichen zu rechnen ist besonders praktisch. Um beispielsweise das gesamte Alphabet auszugeben zählen Sie einfach vom Buchstaben 'A' bis einschließlich 'Z':
Für eine Erklärung des obigen Quellcodes, lesen Sie bitte das Kapitel Schleifen.
Wenn Sie binäre Operatoren auf Zeichen anwenden ist das Ergebnis (mindestens) vom Typ int. Im folgenden Beispiel wird statt eines Buchstabens, der dazugehörige ASCII-Wert ausgeben. Um also wieder ein Zeichen auszugeben, müssen Sie das Ergebnis wieder in den Zeichentyp casten. (Beachten Sie im folgenden Beispiel, dass die Variable i - im Gegensatz zum vorherigen Beispiel - nicht vom Typ char ist):
int main(){
char zeichen = 'A';
for(int i = 0; i < 26; ++i){
std::cout << zeichen + i << ' '; // Ergebnis int
}
std::cout << std::endl;
for(int i = 0; i < 26; ++i){
std::cout << static_cast< char >(zeichen + i); // Ergebnis char
}
}
ABCDEFGHIJKLMNOPQRSTUVWXYZ