Informatik Vorlesung 4 10.11.2004 Gleitkommazahlen (elementare Datentypen float und double) Typenkonversionen (implizit und explizit) Elementarer Datentyp bool Gleitkommazahlen (IEEE 754 Standard) single precision (float) 32-Bit 1Vorzeichenbit, 8 Bit Exponent, 23 Bit Mantisse double precision (double) 64-Bit 1Vorzeichenbit, 11 Bit Exponent, 52 Bit Mantisse float Literale: double Literale Kommazahlen gefolgt von f, zB 1.0f, 3.1415f Kommazahlen, zB 1.0, 3.1415 Komma ist Punkt Der Datentyp float (single precision IEEE 754 Gleitkommazahlen) 32 Bit 8 Bit Exponent 1 Bit Vorzeichen 23 Bit Mantisse Vorzeichen: 0 Positiv 1 Negativ 8 Bit Exponent: Der Exponent wird nicht wie eine ganze Zahl gespeichert, sondern ganzzahlige Repräsentierung plus Bias (127) Bsp: 0 wird dargestellt durch 01111111 (127) Achtung: 11111111 (255 ganze Zahl, bzw. 128 als Exponent) und 00000000 (0 als ganze Zahl bzw. -127 als Exponent) sind keine zulässigen Exponenten! Diese Exponenten um spezielle Zahlen zu kodieren: 1) Null: Vorzeichen Bit 0 oder 1, Exponent 00000000, Mantisse 0…0 2) Denormalisierte Zahlen: Vorzeichen Bit 0 oder 1, Exponent 00000000 Mantisse != 0…0 3) +- Unendlich Vorzeichen Bit 0 oder 1, Exponent 11111111 Mantisse 00000000 4) NAN (Not a Number) Vorzeichen Bit 0 oder 1, Exponent 11111111 Mantisse != 0…0 Bsp: Berechnung der N-ten harmonischen Zahl HN N 1 N 1 Mathematisch äquivalente Repräsentierung HN i 1 i i 1 N i 1 1 1 Euler-Mascheroni Zahl ~0.57721566… Es gilt: H N ln n 2n 1 2*n Programm 1 // Programm: harmonic.C // Berechnung der N-ten harmonischen Zahl auf zwei Arten. #include <iostream> int main() { // Eingabe std::cout << "Welche harmonische Zahl H_N (1 <= N <= " << std::numeric_limits<unsigned int>::max() << ") ? "; unsigned int N; std::cin >> N; // Berechnung der harmonischen Zahl beginnend bei 1 float sum1 = 0.0f; for (unsigned int i = 1; i <= N; ++i) sum1 += 1.0f / i; // Berechnung der harmonischen Zahl beginnend bei N float sum2 = 0.0f; for (unsigned int i = N; i >= 1; --i) sum2 += 1.0f / i; // Ausgabe std::cout << "Vorwaertssumme = " << sum1 << "\n" << "Rueckwaertssumme = " << sum2 << std::endl; return 0; } Rückwärtssumme viel genauer als Vorwärtssumme, da bei der Rückwärtssumme kleinere Terme durch „runden“ verloren gehen, als bei der Vorwärtssumme. Bsp: Berechnung von 1 1 1 1 1 1 i 1 i 1 i i 2 i i 1 i i 1 i 1 i 1 Benutzen um 1 auf 1/10000 zu approximieren Programm 2 // Programm: one.C // Berechnet etwas umstaendlich die Zahl eins. #include <iostream> int main() { // summiere solange, bis der aktuelle Fehler error // kleiner wird als max_error // oo // -1 // Da \ ------------- = 1 , bricht die Schleife // / i * (i + 1) // -// i = 1 // // nach endlich vielen Iterationen ab. float sum = 0.0f; // Partialsumme float error; // momentaner Fehler const float max_error = 0.0001f; // Fehlerschranke unsigned int i = 1; do { sum += 1.0f / (i * (i + 1.0f)); ++i; error = 1.0f - sum; std::cout << "aktueller Fehler ist " << error << std::endl; } while (error > max_error); // Ausgabe: std::cout << "Summe = " << sum << ".\n" << "Fehler = " << error << std::endl; return 0; } Programm gibt endlos „aktueller Fehler ist 0.000147283“ aus (Endlosschlaufe). Typkonversion Wir hatten gesehen 1.0f / i (Ergebnis ist vom Typ float) wobei i vom Typ unsigned int war. Regel: Bei arithmetischen Ausdrücken, die verschiedene Typen beinhalten wird auf den allgemeinsten Typ umgewandelt. Reihenfolge der Allgemeinheit: int unsigned int float double Achtung: Bei Typenumwandlung kann Genauigkeit verloren gehen zB. Int float : Es muss gerundet werden. Programm 3 #include <iostream> #include <limits> #include <cassert> Int main() { const int max = std::numeric_limits<int>::max(); float i1 = imax; double i2 = imax; assert (int(i2) == imax); std::cout << “i1 – i2 = “ << i1 – i2 << std::endl; unsigned int u = 2u; i1 = imax + u; i2 = imax + u; std::cout << “i1 – i2 = “ << i1 – i2 << std::endl; return 0; } Programm gibt “i1 – i2 = 1” (float – double) und „i1 – i2 = -1“ (unsigned int – double) aus. Runden: imax (auf unserer Architektur 231-1) Kann nicht verlustfrei als dargestellt werden. Nicht einfach die letzten Bits Abschneiden bei Konversion von int nach float, sondern round to nearest, d.h. Gleitkommazahl mit kleinster Differenz zur ganzen Zahl wird genommen. In unserem Beispiel wird imax durch 231 (was in float darstellbar ist) approximiert. i1 – i2 = 231 – (231 – 1) == 1 Zweites Beispiel: imax + 2u ist vom Typ unsigned int und hat den Wert 2 31 + 1 i1 vom float wird durch round to nearest auf 231 abgerundet i2 vom double enthält immer noch 231 + 1 i1 – i2 == 231 – (231 + 1) == -1 Bem: Falls nächste float Zahl nicht eindeutig, dann rount to even, d.h. letztes signifikantes Bit ist 0. Der elementare Datentyp bool Datentyp von Wahrheitswerten Literale: true, false Operatoren: && (logisches und), || (logisches oder) Boole’sche Ausdrücke 5 *13 3 >10 14 ||4 1 !=9 2 &&5 3 +12 4 >=10 7 ||4 2 <10 1 +12 1.1f Prioritäten Auswertungsbaum gemäss Prioritäten und Assoziativitäten. Short Circuit Evaluation, die Auswertung eines Boole’schen Ausdrucks wird Abgebrochen, sobald Ergebnis feststeht. 5 * 3 > 14 || 1 != 2 … 15 > 14 || 1 != 2 … true || 1 != 2 … true || 2 < 1 + 1.1f true || 2 < 1 + 1.1f || 2 < 1 + 1.1f || 2 < 1 + 1.1f