Informatik 1 (251-0832-00) D-MAVT F2010 Operatoren, Auswertungsreihenfolge, Fliesskommazahlen Yves Brise 20100310 - Übungsstunde 2 Kommandozeile (UNIX) Textbasiertes “Fenster” zu den Innereien ihres Computers Siehe unix_intro.pdf • Manuelles Compilieren von Programmen • Aufrufen von Programmen • Dateiverwaltung cd, ls, mv, cp, g++ • Unter Windows: cmd.exe Yves Brise 20100310 - Übungsstunde 2 Inhalt Ziele: • Beherschen aller Operatoren • Richtiger Umgang mit Fliesskommazahlen Heute nicht (siehe Vorlesung): • Character processing, ASCII • Konstanten • Gültigkeitsbereiche (Scopes) • Funktionen Yves Brise 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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 { ... } 20100310 - Ü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. p!=0 && p->a > 0 Yves Brise ! 20100310 - Ü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 20100310 - Übungsstunde 2 Auswertungsreihenfolge Klammerung ergibt Ausdrucksbaum: 9 * celsius / 5 + 32 <= 32 + (((9 * celsius) / 5) + 32) <= (32 + o) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius / 5 + 32 <= 32 + (((9 * celsius) / 5) + 32) <= (32 + o) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius / 5 + 32 <= 32 + (((9 * celsius) / 5) + 32) <= (32 + o) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius (((9 * 10.0 / 5 + 32 <= 32 + ) / 5) + 32) <= (32 + o) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius (((9 * 10.0 / 5 + 32 <= 32 + ) / 5) + 32) <= (32 + 0) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 (( * celsius 90.0 / 5 + 32 <= 32 + / 5) + 32) <= (32 + 0) Yves Brise 20100310 - Übungsstunde 2 o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 (( * celsius 90.0 / 5 + 32 <= 32 / 5) + 32) <= Yves Brise 20100310 - Übungsstunde 2 32 + o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 ( * celsius / 5 18.0 + 32 <= 32 + 32) <= Yves Brise 20100310 - Übungsstunde 2 32 + o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius / 5 + 50.0 Yves Brise 32 <= 32 <= 20100310 - Übungsstunde 2 32 + o Auswertungsreihenfolge Auswertung von den Blättern zur Wurzel... 9 * celsius / 5 + 32 <= 32 false Yves Brise 20100310 - Ü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. x++ + x → 2x oder 2x+1? “Guter” Ausdruck: Jede gültige Reihenfolge führt zum selben Ergebnis. Yves Brise 20100310 - Ü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 20100310 - Ü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 20100310 - Übungsstunde 2 IEEE754, float, double float: double: (−1)V · 2E −127 · (1.M) V E −1023 (−1) · 2 Yves Brise · (1.M) 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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 20100310 - Ü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. Resultat kann fälschlicherweise 0 sein. Stichwort: Auslöschung, Cancellation Yves Brise 20100310 - Ü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 20100310 - Ü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 20100310 - Übungsstunde 2 GOTO label1: // do something goto label2; // do something label2: // do something goto label1; Wird nicht mehr oft verwendet, da ein übermässiger Gebrauch zu schwierig zu lesendem Code führt, sog. SpaghettiCode.Verhalten kann immer auch mit anderen Kontrollstrukturen erreicht werden. Edsger Dijkstra: “A Case Against the Go To Statement”, wurde 1968 als “The Go To Statement Considered Harmful” veröffentlicht. Yves Brise 20100310 - Ü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. Yves Brise 20100310 - Übungsstunde 2