Informatik 1 (251-0832-00) D-MAVT F2011 Operatoren, Auswertungsreihenfolge, Fliesskommazahlen Yves Brise 20110309 - Übungsstunde 2 Inhalt Ziele: • Casting, Typenkonvertierung • Mehr Operatoren • Fliesskommazahlen Heute nicht (siehe Vorlesung): • Character processing, ASCII • Konstanten • Gültigkeitsbereiche (Scopes) • Funktionen Yves Brise 20110309 - Übungsstunde 2 To Be Discussed • Updates in Ubuntu ausstellen. Gefahr des Systemzusammenbruchs. System→Update Manager→Settings→(Passwort eingeben: Informatik)→Häkchen bei “Check for updates”, “Important Security updates” und “Recommended Updates” weg. • Probleme mit Vollbildmodus und/oder gemeinsamem Ordner, siehe http://info1-mavt.inf.ethz.ch/ virtualbox.php • Musterlösungen vom letzten Jahr. Yves Brise 20110309 - Übungsstunde 2 Explizite Typenkonvertierung Sogenannte cast expressions, übliche Schreibweise: type(expr) oder (type)expr Bsp: int(9.5+0.6) Es wird abgerundet (double)2+1.5f Wie unärer Operator (double)(7/3) Beide Klammern erlaubt Eigentlich besser: static_cast<int>(9.5+0.6) Yves Brise 20110309 - Übungsstunde 2 rvalues und lvalues Definition Ein lvalue ist ein Ausdruck, der eine Adresse hat (z.B.Variable). Alles andere sind rvalues (z.B. Literal). • Die Bezeichnungen kommen daher, dass nur ein Ausdruck mit Adresse auf der linken Seite einer Zuweisung stehen darf (vgl. left und right) • Operatoren definieren, ob sie einen lvalue brauchen oder nicht. • Ein lvalue kann problemlos in einen rvalue konvertiert werden. Es wird einfach der Wert and der Adresse des lvalue geholt. Yves Brise 20110309 - Übungsstunde 2 Zuweisungsoperator lvalue int a; a = 1; Zuweisungsoperator rvalue Zuweisung ist rechts-assoziativ und gibt einen lvalue zurück, Bsp. a = b = 1 a = (b = 1) Es gibt noch die Varianten += -= *= /= %= Bsp. a += 1 a = a + 1 Single Modification Rule Eine Variable darf in einem Ausdruck nur einmal verändert werden, z.B. x = ++x + 1 ist nicht gut. Yves Brise 20110309 - Übungsstunde 2 Inkrement und Dekrement int a = 1; ++a; a--; Prefix Inkrement Postfix Dekrement • Der Effekt von Inkrement ist, dass er die Variable um eins erhöht, Dekrement erniedrigt die Variable um eins. • Prefix: lvalue → lvalue, gibt neuen Wert zurück. • Postfix: lvalue → rvalue, gibt alten Wert zurück. • Die Prefix Form ist effizienter. • Gibt es nicht nur für int. Besonders interessant auch für eigene Datentypen (Klassen) via Overloading und für Iteratoren. Yves Brise 20110309 - Übungsstunde 2 Relationale Operatoren Damit können Vergleiche angestellt werden. Die Operanden sind zwei rvalues des selben Typs, die Rückgabe ist ein bool. Die Operatoren sind == != < <= > >= Regel: • Relationale Operatoren binden schwächer als aritmethische und stärker als logische. • Sind links-assoziativ, Bsp. a < b == c (a < b)== c Yves Brise if (a >= 0) { ... } else if (a == 1) { ... } else { ... } 20110309 - Übungsstunde 2 Logische Operatoren Operieren auf Wahrheitswerten (bool). Wir benötigen UND (&&) und ODER (||) und nicht (!). Regel: ! bindet stärker als &&, stärker als || Bsp: a&&!b||c (a&&(!b))||c && true false true true false false false false true false true true true false true false || Short-circuit Evaluation Ausdrücke werden von links nach rechts ausgewertet. Wenn schon klar ist, wie der Wert des gesamten Ausdrucks ist, wird der zweite Operand nicht mehr ausgewertet. Bsp. x!=0 && 1/x > 0.5 Yves Brise ! 20110309 - Übungsstunde 2 true false false true Die ganze Wahrheit über Operatoren Präzedenz Operator Stelligkeit Assoziativität r/l-value 2 ++ -- (postfix) 1 links l→r 2 3 3 3 3 5 6 8 9 13 14 16 static_cast ++ -- (prefix) +! (type) */% +< <= > >= != == && || += -= *= /= %= = 1 1 1 1 1 2 2 2 2 2 2 2 links rechts rechts rechts rechts links links links links links links rechts r→r l→l r→r r→r r→r rxr→r rxr→r rxr→r rxr→r rxr→r rxr→r lxr→l http://en.wikipedia.org/wiki/Operators_in_C_and_C++#Operator_precedence Yves Brise 20110309 - Übungsstunde 2 Auswertungsreihenfolge Klammerung ergibt Ausdrucksbaum: 9 * celsius / 5 + 32 <= 32 + (((9 * celsius) / 5) + 32) <= (32 + o) Yves Brise 20110309 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius / 5 + 32 <= 32 + (((9 * 90.0 (( ( celsius) 10.0 18.0) / 5) 50.0 + 32)false <= (3232 + 0) o) Yves Brise 20110309 - Übungsstunde 2 o Auswertungsreihenfolge Gültige Reihenfolge: Jeder Knoten wird erst nach seinen Kindern ausgewertet. Kinder Knoten Vorsicht: Die Reihenfolge ist nicht immer eindeutig bestimmt. Besonders relevant bei “effektvollen” Operatoren. Bsp. Funktionsaufrufe: f() + g() “Guter” Ausdruck: Jede gültige Reihenfolge führt zum selben Ergebnis. Yves Brise 20110309 - Übungsstunde 2 Schnellübung Klammern & Auswerten! Typ, Wert? int a(2), b(0); float s(1.5f); bool t(true), f(false); a) b) c) d) e) f) g) h) i) j) k) l) t = a - 1 < b t || s > 0 && f 1 + 1 * 6 / 4 == 3 && 2.1 / 1.3 < 1.8 12 / 6 / 2 * 3.0 + 1 < 4 || 2.0 * 2 > 1 11 * 19 % 21 * 13 % 23 * 3 % 3 (++a - 1) / 2 a++ - 1 / 2 a != 2 && a - 1 != 1 || a + 1 == 3 17 / 2 == 8.5 && 7 * 3 == 21.0 s = a = 4 / 5 t || 127.37 * s && b == 0 int(8.5) - int(7.6) / ++ b Yves Brise 20110309 - Übungsstunde 2 Schnellübung Lösung int a(2), b(0); float s(1.5f); bool t(true), f(false); a) b) c) d) e) f) g) h) i) j) k) l) t=((a-1)<b); // bool, false t||((s>0)&&f); // bool, true ((1+((1*6)/4)))==3)&&((2.1/1.3)<1.8); // bool, false (((((12/6)/2)*3.0)+1)<4)||((2.0*2)>1); // bool, true (((((11*19)%21)*13)%23)*3)%3; // int, 0 ((++a)-1)/2; // int, 1 (a++)-(1/2); // int, 2 ((a!=2)&&((a-1)!=1))||((a+1)==3); // bool, true ((17/2)==8.5)&&((7*3)==21.0); // bool, false s=(a=(4/5)); // float, 0.0f t||(((127.37*s)&&b)==0); // bool, true int(8.5)-(int(7.6)/(++b)); // int, 1 Yves Brise 20110309 - Übungsstunde 2 IEEE754, float, double float: double: (−1)V · 2E −127 · (1.M) V E −1023 (−1) · 2 Yves Brise · (1.M) 20110309 - Übungsstunde 2 Rechnen mit Fliesskommazahlen Approximation der Euler-Konstante ∞ � 1 e= i! i=0 // Program: euler.cpp // Approximate Euler's constant e. #include <iostream> std::cout.precision(10); int main () { // values for term i, initialized for i = 0 float t = 1.0f; // 1/i! float e = 1.0f; // i-th approximation of e std::cout << "Approximating the Euler constant...\n"; // steps 1,...,n for (unsigned int i = 1; i < 10; ++i) { e += t /= i; // compact form of t = t / i; e = e + t; std::cout << "Value after term " << i << ": " << e << "\n"; } return 0; } Richtig e=2,718281828459... Yves Brise 20110309 - Übungsstunde 2 Löcher im Wertebereich // Program: diff.cpp // Check subtraction of two floating point numbers #include <iostream> int main() { // Input float n1; std::cout << "First number std::cin >> n1; float n2; std::cout << "Second number std::cin >> n2; =? "; Eingabe: 1.5 =? "; Eingabe: 1.0 Beispiel diff.cpp float d; std::cout << "Their difference =? "; std::cin >> d; Eingabe: 0.5 // Computation and output std::cout << "Computed difference - input difference = " << n1 - n2 - d << ".\n"; return 0; Ausgabe: 0 } Yves Brise 20110309 - Übungsstunde 2 Löcher im Wertebereich // Program: diff.cpp // Check subtraction of two floating point numbers #include <iostream> int main() { // Input float n1; std::cout << "First number std::cin >> n1; float n2; std::cout << "Second number std::cin >> n2; =? "; Eingabe: 1.1 =? "; Eingabe: 1.0 Beispiel diff.cpp float d; std::cout << "Their difference =? "; std::cin >> d; Eingabe: 0.1 // Computation and output std::cout << "Computed difference - input difference = " << n1 - n2 - d << ".\n"; return 0; } Yves Brise Ausgabe: 2.23517e-08 20110309 - Übungsstunde 2 IEEE754, Goldene Regel 1 Regel 1: Teste keine Fliesskommazahlen auf Gleichheit, wenn mindestens eine das Ergebnis einer Rundungsoperation ist. for (float i = 0.1; i != 1.0; i += 0.1){ std::cout << i << “\n”; } Klingt theoretisch gut, aber in der Praxis Endlosschleife, da (0.1) = ˆ (0.00011) dez Yves Brise bin 20110309 - Übungsstunde 2 IEEE754, Goldene Regel 2 Regel 2: Vermeide die Addition von Zahlen sehr unterschiedlicher Grösse. Beispiel Harmonische Zahl n � 1 lim Hn ≈ ln n Hn = n→∞ i i=1 Berechnung vorwärts: Berechnung rückwärts: Siehe harmonic.cpp ... 1 1 1 Hn = 1 + + + ... + 2 3 n 1 1 Hn = + + ... + 1 n n−1 Yves Brise 20110309 - Übungsstunde 2 IEEE754, Goldene Regel 3, Auslöschung Regel 3: Vermeide die Subtraktion von Zahlen sehr ähnlicher Grösse. Bsp. Berechnung der Diskriminante einer quadratischen � Gleichung: b 2 − 4ac Problem: Die involvierten Zahlen können das Ergebins einer Rundungsoperation sein. Die Subtraktion kann den Fehler von wenig signifikanten Bits zu signifikatnen Bits erheben. Das Resultat kann um Grössenordnungen falsch sein. Yves Brise 20110309 - Übungsstunde 2 Excel 2007 Bug Excel sagt: 77.1 · 850 = 100000 Richtiges Resultat: 77.1 · 850 = 65535 ? Eigentlich nur Anzeigefehler: Eine von zwölf (laut Micorsoft) Zahlen nahe an 65535, die falsch ins Dezimalsystem umgewandelt werden. Intern wird richtig weiter gerechnet. Aber: Die zugrunde liegende Ursache ist natürlich, dass die Berechnung mit Fliesskommazahlen nicht exakt durchführbar ist. Yves Brise 20110309 - Übungsstunde 2 string vs. std::cin Problem: Es wird nicht genügend Speicher alloziert. #include <iostream> #include <string> int main() { string s; std::cin >> s; } string s(”abcdef...”); string s(10,’0’); std::cout << s.capacity(); s.reserve(10); Yves Brise 20110309 - Übungsstunde 2 Tipps zur Serie 2 Aufgaben 1, 2 und 3: Siehe Schnellübungen. Aufgaben 4: Schritt für Schritt durchgehen und von Hand ausrechnen. Aufgaben 5: Einlesen von 4 Werten (std::cin) und dann einfache Berechnung mit Ausgabe. Versuchen Sie, das Programm selbsterklärend zu machen, d.h. sinnvolle Anfragen an den Benutzer zu stellen. Und: Kommentare nicht vergessen! Yves Brise 20110309 - Übungsstunde 2