Why programming? Do I study computer science or what ... Mathematics used to be the lingua franca of the natural There are programs for everything ... sciences on all universities. Today this is computer I am not interested in programming ... science. because computer science is a mandatory subject here, Lino Guzzella, president of ETH Zurich, NZZ Online, 1.9.2017 unfortunately... . . . 41 42 This is why programming! Programming Languages Any understanding of modern technology requires knowledge about the fundamental operating principles of a computer. The language that the computer can understand (machine Programming (with the computer as a tool) is evolving a cultural language) is very primitive. technique like reading and writing (using the tools paper and Simple operations have to be subdivided into many single steps pencil) The machine language varies between computers. Programming is the interface between engineering and computer science – the interdisciplinary area is growing constantly. Programming is fun! 43 44
Higher Programming Languages Programming langauges – classification Differentiation into Compiled vs. interpreted languages can be represented as program text that C++ , C#, Pascal, Modula, Oberon, Java vs. can be understood by humans Python, Tcl, Matlab is independent of the computer model Higher programming languages vs. Assembler → Abstraction! Multi-purpose programming languages vs. single purpose programming languages Procedural, object oriented , functional and logical languages. 45 46 Why C++ ? Why C++ ? Other popular programming languages: Java, C#, Objective-C, Modula, Oberon, Python . . . Over the years, C++’s greatest strength and its greatest weakness has been its C-Compatibility – B. Stroustrup B. Stroustrup, Design and Evolution of C++, Kap. 4.5 General consensus: „The” programming language for systems programming: C C has a fundamental weakness: missing (type) safety 47 48
Why C++ ? Deutsch vs. C++ Deutsch Es ist nicht genug zu wissen, man muss auch anwenden. C++ equips C with the power of the abstraction of a higher (Johann Wolfgang von Goethe) programming language In this course: C++ introduced as high level language, not as better C C++ Approach: traditionally procedural → object-oriented. // computation int b = a ∗ a; // b = a^2 b = b ∗ b; // b = a^4 49 50 Syntax and Semantics C++ : Kinds of errors illustrated with German sentences Das Auto fuhr zu schnell. Syntaktisch und semantisch korrekt. DasAuto fuh r zu sxhnell. Syntaxfehler: Wortbildung. Like our language, programs have to be formed according to certain rules. Rot das Auto ist. Syntaxfehler: Satzstellung. Syntax: Connection rules for elementary symbols (characters) Man empfiehlt dem Dozenten Syntaxfehler: Satzzeichen fehlen . Semantics: interpretation rules for connected symbols. nicht zu widersprechen Sie ist nicht gross und rothaarig. Syntaktisch korrekt aber mehrdeutig. [kein Analogon] Corresponding rules for a computer program are simpler but also more strict because computers are relatively stupid. Syntaktisch korrekt, doch semantisch fehlerhaft: Die Auto ist rot. Falscher Artikel. [Typfehler] Syntaktisch und grammatikalisch korrekt! Semantisch Das Fahrrad gallopiert schnell. fehlerhaft. [Laufzeitfehler] Syntaktisch und semantisch korrekt. Semantisch Manche Tiere riechen gut. mehrdeutig. [kein Analogon] 51 52
Syntax and Semantics of C++ Syntax and semantics of C++ Syntax The ISO/IEC Standard 14822 (1998, 2011,...) What is a C++ program? is the “law” of C++ Is it grammatically correct? defines the grammar and meaning of C++ programs contains new concepts for advanced programming . . . Semantics . . . which is why we will not go into details of such concepts What does a program mean ? What kind of algorithm does a program implement? 53 54 Programming Tools Language constructs with an example constants Comments/layout Editor: Program to modify, edit and store C++ program texts identifiers, names Include directive Compiler: program to translate a program text into machine objects the main function language Values effects expressions Computer: machine to execute machine language programs Types and functionality L- and R- values Operating System: program to organize all procedures such as literals operators file handling, editor-, compiler- and program execution. variables statements 55 56
The first C++ program Most important ingredients... Behavior of a Program // Program: power8.cpp At compile time: // Raise a number to the eighth power. program accepted by the compiler (syntactically correct) #include <iostream> int main(){ Compiler error // input std::cout << "Compute a^8 for a =? "; int a; During runtime: Statements: Do something (read in a )! std::cin >> a; // computation correct result int b = a ∗ a; // b = a^2 Expressions: Compute a value ( a 2 )! incorrect result b = b ∗ b; // b = a^4 // output b ∗ b, i.e., a^8 program crashes std::cout << a << "^8 = " << b ∗ b << "\n"; return 0; program does not terminate (endless loop) } 57 58 “Accessories:” Comments Comments and Layout // Program: power8.cpp Comments // Raise a number to the eighth power. are contained in every good program. #include <iostream> int main() { document what and how a program does something and how it // input comments should be used, std::cout << "Compute a^8 for a =? "; int a; are ignored by the compiler std::cin >> a; // computation Syntax: “double slash” // until the line ends. int b = a ∗ a; // b = a^2 The compiler ignores additionally b = b ∗ b; // b = a^4 // output b ∗ b, i.e., a^8 Empty lines, spaces, std::cout << a << "^8 = " << b ∗ b << "\n"; return 0; Indendations that should reflect the program logic } 59 60
Comments and Layout “Accessories:” Include and Main Function // Program: power8.cpp // Raise a number to the eighth power. include directive #include <iostream> The compiler does not care... int main() { declaration of the main function // input std::cout << "Compute a^8 for a =? "; #include <iostream> int a; int main(){std::cout << "Compute a^8 for a =? "; std::cin >> a; int a; std::cin >> a; int b = a * a; b = b * b; // computation std::cout << a << "^8 = " << b*b << "\n";return 0;} int b = a ∗ a; // b = a^2 b = b ∗ b; // b = a^4 // output b ∗ b, i.e., a^8 std::cout << a << "^8 = " << b ∗ b << "\n"; ... but we do! return 0; } 61 62 Include Directives The main Function the main -function C++ consists of is provided in any C++ program the core language standard library is called by the operating system like a mathematical function ... in-/output (header iostream) mathematical functions (cmath) arguments ... return value ... but with an additional effect #include <iostream> Read a number and output the 8th power. makes in- and output available 63 64
Statements: Do something! Statements int main() { // input std::cout << "Compute a^8 for a =? "; int a; building blocks of a C++ program expression statements std::cin >> a; are executed (sequentially) // computation int b = a ∗ a; // b = a^2 end with a semicolon b = b ∗ b; // b = a^4 Any statement has an effect (potentially) // output b ∗ b, i.e., a^8 std::cout << a << "^8 = " << b ∗ b << "\n"; return statement return 0; } 65 66 Expression Statements Return Statements have the following form: do only occur in functions and are of the form expr; return expr; where expr is an expression where expr is an expression Effect is the effect of expr , the value of expr is ignored. specify the return value of a function Example: return 0; Example: b = b*b; 67 68
Statements – Effects Values and Effects int main() { effect: output of the string Compute ... // input std::cout << "Compute a^8 for a =? "; int a; determine what a program does, std::cin >> a; Effect: input of a number stored in a are purely semantical concepts: // computation Effect: saving the computed value of a*a into b int b = a ∗ a; // b = a^2 Symbol 0 means Value 0 ∈ ❩ b = b ∗ b; std::cin >> a; means effect "read in a number" // b = a^4 Effect: saving the computed value of b*b into b depend on the program state (memory content, inputs) // output b ∗ b, i.e., a^8 std::cout << a << "^8 = " << b ∗ b << "\n"; return 0; } Effect: return the value 0 Effect: output of the value of a and the computed value of 69 70 Statements – Variable Definitions Declaration Statements int main() { // input std::cout << "Compute a^8 for a =? "; introduce new names in the program, declaration statement int a; consist of declaration and semicolon std::cin >> a; type // computation names Example: int a; int b = a ∗ a; // b = a^2 b = b ∗ b; can initialize variables // b = a^4 // output b ∗ b, i.e., a^8 Example: int b = a * a; std::cout << a << "^8 = " << b ∗ b << "\n"; return 0; } 71 72
Types and Functionality Fundamental Types int : C++ comprises fundamental types for C++ integer type integers ( int ) corresponds to ( ❩ , + , × ) in math natural numbers ( unsigned int ) real numbers ( float , double ) In C++ each type has a name and boolean values ( bool ) a domain (e.g. integers) ... functionality (e.g. addition/multiplication) 73 74 Literals Variables Example represent (varying) values, represent constant values have int a; defines a variable with have a fixed type and value name: a name are "syntactical values". type type: int value Examples: address value: (initially) undefined 0 has type int , value 0 . are "visible" in the program Address: determined by compiler 1.2e5 has type double , value 1 . 2 · 10 5 . context. 75 76
Objects Identifiers and Names represent values in main memory (Variable-)names are identifiers have type , address and value (memory content at the address) allowed: A,...,Z; a,...,z; 0,...,9; _ can be named (variable) ... ... but also anonymous. First symbol needs to be a character. There are more names: Remarks A program has a fixed number of variables. In order to be able to deal with a std::cin (Qualified identifier) variable number of value, it requires "anonymous" addresses that can be address via temporary names. 77 78 Expressions: compute a value! Expressions Building Blocks composite expression // input std::cout << "Compute a^8 for a =? "; represent Computations int a; are either primary ( b ) std::cin >> a; or composed ( b*b ). . . // computation int b = a ∗ a; // b = a^2 . . . from different expressions, using operators Two times composed expression b = b * b; // b = a^4 have a type and a value // output b ∗ b, i.e., a^8 Analogy: building blocks std::cout << a<< "^8 = " << b * b << ". \ n"; return 0; Four times composed expression 79 80
Expressions Expressions have type , value und effect (potentially). represent computations are primary or composite (by other expressions and operations) Example Example a * a b = b * b a * a type: int (type of the operands) type: int (Typ der Operanden) composed of Value: product of a and a Value: product of b and b variable name, operator symbol,variable name variable name: primary expression Effect: none. effect: assignment of the product value to b can be put into parantheses The type of an expression is fixed but the value and effect are only a * a is equivalent to (a * a) determined by the evaluation of the expression 81 82 L-Values and R-Values L-Values and R-Values R-Value // input L-Wert (“ L eft of the assignment operator”) std::cout << "Compute a^8 for a =? "; Expression with address int a; L-value (expression + address) std::cin >> a; Value is the content at the memory location according to the L-value (expression + address) type of the expression. // computation int b = a ∗ a; // b = a^2 L-Value can change its value (e.g. via assignment) b = b * b; // b = a^4 R-Value Example: variable name // output b ∗ b, i.e., a^8 std::cout << a<< "^8 = " << b * b << ". \ n"; return 0; R-Value (expression that is not an L-value) 83 84
L-Values and R-Values Operators and Operands Building Blocks left operand (output stream) output operator right operand (string) R-Wert (“ R ight of the assignment operator”) // input std::cout << "Compute a^8 for a =? "; Expression that is no L-value int a; Example: literal 0 std::cin >> a; right operand (variable name) input operator Any L-Value can be used as R-Value (but not the other way // computation left operand (input stream) int b = a ∗ a; round) // b = a^2 b = b ∗ b; // b = a^4 An R-Value cannot change its value assignment operator // output b ∗ b, i.e., a^8 std::cout << a << "^8 = " << b * b << "\n"; return 0; multiplication operator 85 86 Operators Multiplication Operator * Operators expects two R-values of the same type as operands (arity 2) "returns the product as R-value of the same type", that means combine expressions ( operands ) into new composed formally: expressions The composite expression is an R-value; its value is the product of the specify for the operands and the result the types and if the have value of the two operands to be L- or R-values. have an arity Examples: a * a and b * b 87 88
Assignment Operator = Input Operator >> L eft operand is L -value, left operand is L-Value (input stream) R ight operand is R -value of the same type. right operand is L-Value Assigns to the left operand the value of the right operand and assigns to the right operand the next value read from the input returns the left operand as L-value stream, removing it from the input stream and returns the input stream as L-value Examples: b = b * b and a = b Example std::cin >> a (mostly keyboard input) Attention, Trap! Input stream is being changed and must thus be an L-Value. The operator = corresponds to the assignment operator of mathematics ( := ), not to the comparison operator ( = ). 89 90 Output Operator << Output Operator << Why returning the output stream? left operand is L-Value ( output stream ) allows bundling of output right operand is R-Value std::cout << a << "^8 = " << b * b << "\n" outputs the value of the right operand, appends it to the output stream and returns the output stream as L-Value is parenthesized as follows Example: std::cout << a (mostly console output) ((((std::cout << a) << "^8 = ") << b * b) << "\n") The output stream is being changed and must thus be an L-Value. std::cout << a is the left hand operand of the next << and is thus an L-Value that is no variable name 91 92
Celsius to Fahrenheit // Program: fahrenheit.cpp // Convert temperatures from Celsius to Fahrenheit. #include <iostream> int main() { 2. Integers // Input std::cout << "Temperature in degrees Celsius =? "; int celsius; std::cin >> celsius; Evaluation of Arithmetic Expressions, Associativity and Precedence, Arithmetic Operators, Domain of Types int , unsigned int // Computation and output std::cout << celsius << " degrees Celsius are " << 9 * celsius / 5 + 32 << " degrees Fahrenheit.\n"; return 0; } 93 94 9 * celsius / 5 + 32 Precedence Multiplication/Division before Addition/Subtraction 9 * celsius / 5 + 32 Arithmetic expression, bedeutet contains three literals, a variable, three operator symbols (9 * celsius / 5) + 32 How to put the expression in parentheses? Rule 1: precedence Multiplicative operators ( * , / , % ) have a higher precedence ("bind more strongly") than additive operators ( + , - ) 95 96
Associativity Arity From left to right Rule 3: Arity 9 * celsius / 5 + 32 Unary operators + , - first, then binary operators + , - . bedeutet -3 - 4 ((9 * celsius) / 5) + 32 means Rule 2: Associativity (-3) - 4 Arithmetic operators ( * , / , % , + , - ) are left associative: operators of same precedence evaluate from left to right 97 98 Parentheses Expression Trees Parentheses yield the expression tree Any expression can be put in parentheses by means of (((9 * celsius) / 5) + 32) associativities 9 celsius 5 32 precedences arities (number of operands) * of the operands in an unambiguous way (Details in the lecture / notes). + 99 100
Evaluation Order Evaluation Order "From top to bottom" in the expression tree Order is not determined uniquely: 9 * celsius / 5 + 32 9 * celsius / 5 + 32 9 celsius 5 32 9 celsius 5 32 * * / / + + 101 102 Expression Trees – Notation Evaluation Order – more formally Common notation: root on top 9 * celsius / 5 + 32 Valid order: any node is evaluated after its children E + In C++ , the valid order to be used is not defined. K 1 K 2 / 32 "Good expression": any valid evaluation order leads to the same result. Example for a "bad expression": (a+b)*(a++) * 5 9 celsius 103 104
Evaluation order Arithmetic operations Symbol Arity Precedence Associativity 1 16 right Unary + + Guideline 1 16 right Negation - 2 14 left Multiplication * Avoid modifying variables that are used in the same expression 2 14 left Division / more than once. 2 14 links Modulus % 2 13 left Addition + 2 13 left Subtraction - All operators: [R-value × ] R-value → R-value 105 106 Assignment expression – in more detail Division and Modulus Operator / implements integer division Already known: a = b means Assignment of b (R-value) to a (L-value). 5 / 2 has value 2 Returns: L-value In fahrenheit.cpp What does a = b = c mean? 9 * celsius / 5 + 32 Answer: assignment is right-associative 15 degrees Celsius are 59 degrees Fahrenheit ⇐ ⇒ a = b = c a = (b = c) Mathematically equivalent. . . but not in C++ ! 9 / 5 * celsius + 32 Example multiple assignment: 15 degrees Celsius are 47 degrees Fahrenheit a = b = 0 = ⇒ b=0; a=0 107 108
Division and Modulus Increment and decrement Increment / Decrement a number by one is a frequent operation Modulus-operator computes the rest of the integer division works like this for an L-value: has value 2, has value 1. 5 / 2 5 % 2 expr = expr + 1 . It holds that: Disadvantages has the value of a . (a / b) * b + a % b relatively long expr is evaluated twice (effects!) 109 110 In-/Decrement Operators In-/decrement Operators Post-Increment expr++ Value of expr is increased by one, the old value of expr is returned (as R-value) Pre-increment use arity prec assoz L-/R-value ++expr 1 17 left L-value → R-value Post-increment expr++ Value of expr is increased by one, the new value of expr is returned (as L-value) 1 16 right L-value → L-value Pre-increment ++expr Post-Dekrement 1 17 left L-value → R-value Post-decrement expr-- 1 16 right L-value → L-value Pre-decrement --expr expr-- Value of expr is decreased by one, the old value of expr is returned (as R-value) Prä-Dekrement --expr Value of expr is increased by one, the new value of expr is returned (as L-value) 111 112
In-/Decrement Operators In-/Decrement Operators Is the expression ++expr; ← we favour this Example equivalent to int a = 7; expr++; ? std::cout << ++a << "\n"; // 8 std::cout << a++ << "\n"; // 8 Yes, but std::cout << a << "\n"; // 9 Pre-increment can be more efficient (old value does not need to be saved) Post In-/Decrement are the only left-associative unary operators (not very intuitive) 113 114 C++ vs. ++C Arithmetic Assignments a += b Strictly speaking our language should be named ++C because ⇔ it is an advancement of the language C a = a + b while C++ returns the old C . analogously for -, *, / and % 115 116
Arithmetic Assignments Binary Number Representations Binary representation ("Bits" from { 0 , 1 } ) Gebrauch Bedeutung b n b n − 1 . . . b 1 b 0 += expr1 += expr2 expr1 = expr1 + expr2 -= expr1 -= expr2 expr1 = expr1 - expr2 corresponds to the number b n · 2 n + · · · + b 1 · 2 + b 0 *= expr1 *= expr2 expr1 = expr1 * expr2 Example: 101011 corresponds to 43 . /= expr1 /= expr2 expr1 = expr1 / expr2 %= expr1 %= expr2 expr1 = expr1 % expr2 Least Significant Bit (LSB) Arithmetic expressions evaluate expr1 only once. Most Significant Bit (MSB) Assignments have precedence 4 and are right-associative. 117 118 Binary Numbers: Numbers of the Computer? Binary Numbers: Numbers of the Computer? Truth: Computers calculate using binary numbers. Stereotype: computers are talking 0/1 gibberish 119 120
Computing Tricks Hexadecimal Numbers Numbers with base 16 Hex Nibbles Estimate the orders of magnitude of powers of two. 3 : hex bin dec h n h n − 1 . . . h 1 h 0 0 0000 0 2 10 = 1024 = 1Ki ≈ 10 3 . 1 0001 1 2 0010 2 2 20 = 1Mi ≈ 10 6 , corresponds to the number 3 0011 3 2 30 = 1Gi ≈ 10 9 , 4 0100 4 5 0101 5 2 32 = 4 · (1024) 3 = 4Gi . h n · 16 n + · · · + h 1 · 16 + h 0 . 6 0110 6 7 0111 7 2 64 = 16Ei ≈ 16 · 10 18 . 8 1000 8 9 1001 9 notation in C++: prefix 0x a 1010 10 b 1011 11 Example: 0xff corresponds to 255 . c 1100 12 d 1101 13 e 1110 14 3 Decimal vs. binary units: MB - Megabyte vs. MiB - Megabibyte (etc.) f 1111 15 kilo (K, Ki) – mega (M, Mi) – giga (G, Gi) – tera(T, Ti) – peta(P , Pi) – exa (E, Ei) 121 122 Why Hexadecimal Numbers? Example: Hex-Colors A Hex-Nibble requires exactly 4 bits. Numbers 1, 2, 4 and 8 represent bits 0, 1, 2 and 3. „compact representation of binary numbers” #00FF00 32-bit numbers consist of eight hex-nibbles: 0x00000000 -- 0xffffffff . 0x400 = 1 Ki = 1 ′ 024 . r g b 0x100000 = 1 Mi = 1 ′ 048 ′ 576 . 0x40000000 = 1 Gi = 1 ′ 073 . 741 , 824 . 0x80000000 : highest bit of a 32-bit number is set 0xffffffff : all bits of a 32-bit number are set „ 0x8a20aaf0 is an address in the upper 2G of the 32-bit address space” 123 124
http://www.zanchetta.net/default.aspx?Categorie=ECHIQUIERS&Page=documentations Why Hexadecimal Numbers? Why Hexadecimal Numbers? “For programmers and technicians” (Excerpt of a user manual of the The NZZ could have saved a lot of space ... chess computers Mephisto II , 1981) 125 126 Domain of Type int Domain of the Type int // Program: limits.cpp Representation with B bits. Domain comprises the 2 B integers: // Output the smallest and the largest value of type int. #include <iostream> #include <limits> {− 2 B − 1 , − 2 B − 1 + 1 , . . . , − 1 , 0 , 1 , . . . , 2 B − 1 − 2 , 2 B − 1 − 1 } int main() { On most platforms B = 32 std::cout << "Minimum int value is " << std::numeric_limits<int>::min() << ".\n" For the type int C++ guarantees B ≥ 16 << "Maximum int value is " << std::numeric_limits<int>::max() << ".\n"; return 0; Background: Section 2.2.8 (Binary Representation) in the lecture } notes. For example Where does this partitioning come from? Minimum int value is -2147483648. Maximum int value is 2147483647. Where do these numbers come from? 127 128
Over- and Underflow The Type unsigned int Arithmetic operations ( +,-,* ) can lead to numbers outside the valid domain. Domain { 0 , 1 , . . . , 2 B − 1 } Results can be incorrect! power8.cpp : 15 8 = − 1732076671 All arithmetic operations exist also for unsigned int . Literals: 1u, 17u . . . power20.cpp : 3 20 = − 808182895 There is no error message! 129 130 Mixed Expressions Conversion Operators can have operands of different type (e.g. int and unsigned int ). int Value Sign unsigned int Value ≥ 0 17 + 17u x x Such mixed expressions are of the “more general” type x + 2 B x < 0 unsigned int . int -operands are converted to unsigned int . Using two complements representation, nothing happens in- ternally 131 132
Conversion “reversed” Signed Number Representation (Hopefully) clear by now: binary number representation without sign, e.g. The declaration b 31 · 2 31 + b 30 · 2 30 + · · · + b 0 [ b 31 b 30 . . . b 0 ] u = � int a = 3u; converts 3u to int . Obviously required: use a bit for the sign. The value is preserved because it is in the domain of int ; otherwise Looking for a consistent solution the result depends on the implementation. The representation with sign should coincide with the unsigned solution as much as possible. Positive numbers should arithmetically be treated equal in both systems. 133 134 Computing with Binary Numbers (4 digits) Computing with Binary Numbers (4 digits) Simple Addition Addition with Overflow 2 0010 7 0111 +3 +0011 +9 +1001 5 0101 16 (1)0000 Simple Subtraction Negative Numbers? 5 0101 5 0101 − 3 − 0011 +( − 5) ???? 2 0010 0 (1)0000 135 136
Computing with Binary Numbers (4 digits) Computing with Binary Numbers (4 digits) Simpler -1 Invert! 1 0001 3 0011 +( − 1) 1111 +( − 4) +1100 = 2 B − 1 0 (1)0000 − 1 1111 � Utilize this: 3 0011 a a +? +???? +( − a − 1) ¯ a = 2 B − 1 − 1 1111 − 1 1111 � 137 138 Computing with Binary Numbers (4 digits) Why this works Modulo arithmetics: Compute on a circle 4 Negation: inversion and addition of 1 − a = � ¯ a + 1 + = Wrap around semantics (calculating modulo 2 B 11 ≡ 23 ≡ − 1 ≡ 4 ≡ 16 ≡ . . . 3 ≡ 15 ≡ . . . . . . mod 12 mod 12 mod 12 2 B − a − a = � 4 The arithmetics also work with decimal numbers (and for multiplication). 139 140
Negative Numbers (3 Digits) Two’s Complement a − a Negation by bitwise negation and addition of 1 0 000 000 0 − 2 = − [0010] = [1101] + [0001] = [1110] 1 001 -1 111 2 010 -2 110 Arithmetics of addition and subtraction identical to unsigned arithmetics 3 011 -3 101 3 − 2 = 3 + ( − 2) = [0011] + [1110] = [0001] 4 -4 100 100 Intuitive “wrap-around” conversion of negative numbers. 5 101 6 110 − n → 2 B − n 7 111 Domain: − 2 B − 1 . . . 2 B − 1 − 1 The most significant bit decides about the sign. 141 142 Our Goal int a; std::cin >> a; if (a % 2 == 0) 3. Logical Values std::cout << "even"; else std::cout << "odd"; Boolean Functions; the Type bool ; logical and relational operators; shortcut evaluation Behavior depends on the value of a Boolean expression 143 144
The Type bool in C++ Boolean Values in Mathematics Boolean expressions can take on one of two values: 0 or 1 represents logical values Literals false and true 0 corresponds to “false” Domain { false , true } 1 corresponds to “true” bool b = true; // Variable with value true 145 146 Relational Operators Table of Relational Operators Symbol Arity Precedence Associativity (smaller than) a < b 2 11 left smaller < (greater than) a >= b 2 11 left greater > (equals) 2 11 left smaller equal <= a == b 2 11 left greater equal >= (not equal) a != b 2 10 left equal == 2 10 left unequal != arithmetic type × arithmetic type → bool arithmetic type × arithmetic type → bool R-value × R-value → R-value R-value × R-value → R-value 147 148
AND( x, y ) x ∧ y Boolean Functions in Mathematics Boolean function “logical And” x y AND( x, y ) f : { 0 , 1 } 2 → { 0 , 1 } 0 0 0 f : { 0 , 1 } 2 → { 0 , 1 } 0 1 0 0 corresponds to “false”. 0 corresponds to “false”. 1 0 0 1 corresponds to “true”. corresponds to “true”. 1 1 1 149 150 x ∨ y OR( x, y ) Logical Operator && “logical Or” x y OR( x, y ) 0 0 0 (logical and) f : { 0 , 1 } 2 → { 0 , 1 } a && b 0 1 1 0 corresponds to “false”. 1 0 1 bool × bool → bool corresponds to “true”. 1 1 1 R-value × R-value → R-value int n = − 1; int p = 3; bool b = (n < 0) && (0 < p); // b = true 151 152
NOT( x ) ¬ x Logical Operator || “logical Not” x NOT( x ) (logical or) f : { 0 , 1 } → { 0 , 1 } a || b 0 1 0 corresponds to “false”. 1 0 bool × bool → bool corresponds to “true”. R-value × R-value → R-value int n = 1; int p = 0; bool b = (n < 0) || (0 < p); // b = false 153 154 Logical Operator ! Precedences !b && a (logical not) !b � (!b) && a a && b || c && d bool → bool � R-value → R-value (a && b) || (c && d) a || b && c || d int n = 1; � bool b = !(n < 0); // b = true a || (b && c) || d 155 156
Table of Logical Operators Precedences The unary logical operator ! binds more strongly than binary arithmetic operators. These Symbol Arity Precedence Associativity bind more strongly than 2 6 left Logical and (AND) && relational operators, 2 5 left Logical or (OR) || and these bind more strongly than 1 16 right Logical not (NOT) ! binary logical operators. 7 + x < y && y != 3 * z || ! b 7 + x < y && y != 3 * z || (!b) 157 158 x ⊕ y Completeness: XOR( x, y ) Completeness x y XOR( x, y ) AND , OR and NOT are the boolean 0 0 0 XOR( x, y ) = AND(OR( x, y ) , NOT(AND( x, y ))) . functions available in C++ . 0 1 1 Any other binary boolean function can 1 0 1 x ⊕ y = ( x ∨ y ) ∧ ¬ ( x ∧ y ) . be generated from them. 1 1 0 (x || y) && !(x && y) 159 160
Completeness Proof Completeness Proof Identify binary boolean functions with their characteristic vector. Step 1: generate the fundamental functions f 0001 , f 0010 , f 0100 , f 1000 x y XOR( x, y ) f 0001 = AND( x, y ) 0 0 0 characteristic vector: 0110 f 0010 = AND( x, NOT( y )) 0 1 1 f 0100 = AND( y, NOT( x )) XOR = f 0110 1 0 1 f 1000 = NOT(OR( x, y )) 1 1 0 161 162 Completeness Proof bool vs int : Conversion Step 2: generate all functions by applying logical or → int bool bool can be used whenever int is expected → 1 true – and vice versa. → 0 false Many existing programs use int instead of f 1101 = OR( f 1000 , OR( f 0100 , f 0001 )) → bool int bool → true � = 0 This is bad style originating from the → false Step 3: generate f 0000 0 language C . bool b = 3; // b= true f 0000 = 0 . 163 164
DeMorgan Rules Application: either ... or (XOR) x or y, and not both (x || y) && !(x && y) !(a && b) == (!a || !b) x or y, and one of them not (x || y) && (!x || !y) !(a || b) == (!a && !b) not none and not both !(!x && !y) && !(x && y) ! (rich and beautiful) == (poor or ugly) not: both or none !(!x && !y || x && y) 165 166 Short circuit Evaluation Sources of Errors Logical operators && and || evaluate the left operand first . Errors that the compiler can find: If the result is then known, the right operand will not be evaluated. syntactical and some semantical errors Errors that the compiler cannot find: x != 0 && z / x > y runtime errors (always semantical) ⇒ No division by 0 167 168
Avoid Sources of Bugs Against Runtime Errors: Assertions 1. Exact knowledge of the wanted program behavior assert(expr) ≫ It’s not a bug, it’s a feature !! ≪ 2. Check at many places in the code if the program is still on track! halts the program if the boolean expression expr is false 3. Question the (seemingly) obvious, there could be a typo in the requires #include <cassert> code. can be switched off 169 170 DeMorgan’s Rules Switch off Assertions Question the obvious Question the seemingly obvious! // Prog: assertion2.cpp // use assertions to check De Morgan’s laws. To tell the // compiler to ignore them, #define NDEBUG ("no debugging") // Prog: assertion.cpp // at the beginning of the program, before the #includes // use assertions to check De Morgan’s laws #define NDEBUG #include<cassert> #include<cassert> int main() int main() { { bool x; // whatever x and y actually are, bool x; // whatever x and y actually are, bool y; // De Morgan’s laws will hold: bool y; // De Morgan’s laws will hold: assert ( !(x && y) == (!x || !y) ); assert ( !(x && y) == (!x || !y) ); // ignored by NDEBUG assert ( !(x || y) == (!x && !y) ); assert ( !(x || y) == (!x && !y) ); // ignored by NDEBUG return 0; return 0; } } 171 172
Div-Mod Identity a/b * b + a%b == a Div-Mod identity a/b * b + a%b == a Check if the program is on track. . . . . . and question the obvious! std::cout << "Dividend a =? "; // check input Precondition for the ongoing computation int a; assert (b != 0); std::cin >> a; Input arguments for calcula- // compute result tion std::cout << "Divisor b =? "; int div = a / b; int b; int mod = a % b; std::cin >> b; // check result assert (div ∗ b + mod == a); Div-Mod identity // check input assert (b != 0); Precondition for the ongoing computation ... 173 174 Control Flow up to now linear (from top to bottom) For interesting programs we need “branches” and “jumps” Computation of 1 + 2 + ... + n . 4. Control Structures I ja Input n s := s + i; i ≤ n ? i := 1; s := 0 i := i+ 1 Selection Statements, Iteration Statements, Termination, Blocks nein Output s 175 176
Selection Statements if -Statement If condition is true then state- if ( condition ) ment is executed statement implement branches statement : arbitrary statement ( body of the if statement int a; if -Statement) if-else statement std::cin >> a; condition : convertible to if (a % 2 == 0) bool std::cout << "even"; 177 178 if-else -statement Layout! if ( condition ) If condition is true then state- statement1 ment1 is executed, otherwise else statement2 is executed. int a; statement2 std::cin >> a; condition : convertible to if (a % 2 == 0) int a; bool . std::cout << "even"; Indentation std::cin >> a; else statement1 : body of the if (a % 2 == 0) std::cout << "odd"; Indentation if -branch std::cout << "even"; statement2 : body of the else else -branch std::cout << "odd"; 179 180
Compute 1 + 2 + ... + n Iteration Statements // Program: sum_n.cpp // Compute the sum of the first n natural numbers. #include <iostream> implement “loops” int main() { // input for -statement std::cout << "Compute the sum 1+...+n for n =? "; unsigned int n; while -statement std::cin >> n; do -statement // computation of sum_{i=1}^n i unsigned int s = 0; for (unsigned int i = 1; i <= n; ++i) s += i; // output std::cout << "1+...+" << n << " = " << s << ".\n"; return 0; } 181 182 for -Statement Example for -Statement: Syntax for ( unsigned int i=1; i <= n ; ++i ) s += i; for ( init statement condition ; expression ) statement Assumptions: n == 2 , s == 0 init-statement : expression statement, declaration statement, null i s statement wahr i==1 s == 1 condition : convertible to bool wahr i==2 s == 3 expression : any expression falsch i==3 statement : any statement ( body of the for-statement) s == 3 183 184
for -Statement: semantics Gauß as a Child (1777 - 1855) for ( init statement condition ; expression ) statement Math-teacher wanted to keep the pupils busy with the following task: init-statement is executed condition is evaluated Compute the sum of numbers from 1 to 100 ! true : Iteration starts statement is executed expression is executed Gauß finished after one minute. false: for -statement is ended. 185 186 The Solution of Gauß for -Statement: Termination The requested number is 1 + 2 + 3 + · · · + 98 + 99 + 100 . for (unsigned int i = 1; i <= n; ++i) s += i; Here and in most cases: This is half of expression changes its value that appears in condition . 1 + 2 + · · · + 99 + 100 After a finite number of iterations condition becomes false: + 100 + 99 + · · · + 2 + 1 Termination = 101 + 101 + · · · + 101 + 101 Answer: 100 · 101 / 2 = 5050 187 188
Infinite Loops Halting Problem Infinite loops are easy to generate: Undecidability of the Halting Problem There is no C++ program that can determine for each for ( ; ; ) ; C++ -Program P and each input I if the program P terminates with Die empty condition is true. the input I . Die empty expression has no effect. Die null statement has no effect. This means that the correctness of programs can in general not be automatically checked. 5 ... but can in general not be automatically detected. for ( e; v; e) r; 5 Alan Turing, 1936. Theoretical quesitons of this kind were the main motivation for Alan Turing to construct a computing machine. 189 190 Example: Prime Number Test Blocks Blocks group a number of statements to a new statement Def.: a natural number n ≥ 2 is a prime number, if no {statement1 statement2 ... statementN} d ∈ { 2 , . . . , n − 1 } divides n . Example: body of the main function A loop that can test this: int main() { unsigned int d; ... } for (d=2; n%d != 0; ++d); Example: loop body Observation 1: for (unsigned int i = 1; i <= n; ++i) { After the for -statement it holds that d ≤ n . s += i; std::cout << "partial sum is " << s << "\n"; Observation 2: } n is a prime number if and only if finally d = n . 191 192
Visibility Declaration in a block is not “visible” outside of the block. int main () { 5. Control Statements II { block int i = 2; main block } Visibility, Local Variables, While Statement, Do Statement, Jump std::cout << i; // Error: undeclared name Statements return 0; } „Blickrichtung” 193 194 Control Statement defines Block Scope of a Declaration Potential scope: from declaration until end of the part that contains the declaration. in the block in function body In this respect, statements behave like blocks. int main() { int main() { { int i = 2; int i = 2; scope for (unsigned int i = 0; i < 10; ++i) ... ... block scope s += i; } return 0; std::cout << i; // Error: undeclared name } return 0; in control statement } for ( int i = 0; i < 10; ++i) {s += i; ... } scope 195 196
Scope of a Declaration Automatic Storage Duration Real scope = potential scope minus potential scopes of declarations of symbols with the same name Local Variables (declaration in block) int main() are (re-)created each time their declaration is reached { in main int i = 2; memory address is assigned (allocation) for (int i = 0; i < 5; ++i) potential initialization is executed i 2 in for // outputs 0,1,2,3,4 std::cout << i; are deallocated at the end of their declarative region (memory is // outputs 2 released, address becomes invalid) scope of i std::cout << i; return 0; } 197 198 Local Variables while Statement int main() { int i = 5; while ( condition ) for (int j = 0; j < 5; ++j) { statement std::cout << ++i; // outputs 6, 7, 8, 9, 10 int k = 2; std::cout << −− k; // outputs 1, 1, 1, 1, 1 statement : arbitrary statement, body of the while statement. } condition : convertible to bool . } Local variables (declaration in a block) have automatic storage duration . 199 200
while Statement while -Statement: Semantics while ( condition ) while ( condition ) statement statement is equivalent to condition is evaluated true : iteration starts for ( ; condition ; ) statement is executed statement false : while -statement ends. 201 202 ( n ∈ ◆ ) while -statement: why? Example: The Collatz-Sequence In a for -statement, the expression often provides the progress n 0 = n (“counting loop”) � n i − 1 , if n i − 1 even 2 n i = , i ≥ 1 . for (unsigned int i = 1; i <= n; ++i) , if n i − 1 odd 3 n i − 1 + 1 s += i; n=5: 5, 16, 8, 4, 2, 1, 4, 2, 1, ... (repetition at 1) If the progress is not as simple, while can be more readable. 203 204
The Collatz Sequence in C++ The Collatz Sequence in C++ // Program: collatz.cpp // Compute the Collatz sequence of a number n. n = 27: #include <iostream> 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, int main() 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, { // Input 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, std::cout << "Compute the Collatz sequence for n =? "; 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, unsigned int n; std::cin >> n; 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, // Iteration while (n > 1) { 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, if (n % 2 == 0) 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, n = n / 2; else 10, 5, 16, 8, 4, 2, 1 n = 3 * n + 1; std::cout << n << " "; } std::cout << "\n"; return 0; 205 206 } The Collatz-Sequence do Statement Does 1 occur for each n ? do statement It is conjectured, but nobody can prove it! while ( expression ); If not, then the while -statement for computing the Collatz-sequence can theoretically be an endless loop for some statement : arbitrary statement, body of the do statement. n . expression : convertible to bool . 207 208
do Statement do -Statement: Semantics do do statement statement while ( expression ); while ( expression ); Iteration starts is equivalent to statement is executed. expression is evaluated statement while ( expression ) true : iteration begins false : do -statement ends. statement 209 210 Conclusion do -Statement: Example Calculator Sum up integers (if 0 then stop): Selection (conditional branches ) int a; // next input value if and if-else -statement int s = 0; // sum of values so far Iteration (conditional jumps ) do { for -statement std::cout << "next number =? "; while -statement std::cin >> a; do -statement s += a; Blocks and scope of declarations std::cout << "sum = " << s << "\n"; } while (a != 0); 211 212
Jump Statements break -Statement break; break ; Immediately leave the enclosing iteration statement. continue ; useful in order to be able to break a loop “in the middle” 6 6 and indispensible for switch-statements. 213 214 Calculator with break Calculator with break Sum up integers (if 0 then stop) Suppress irrelevant addition of 0: int a; int a; int s = 0; int s = 0; do { do { std::cout << "next number =? "; std::cout << "next number =? "; std::cin >> a; std::cin >> a; // irrelevant in last iteration: if (a == 0) break; // stop loop in the middle s += a; s += a; std::cout << "sum = " << s << "\n"; std::cout << "sum = " << s << "\n"; } while (a != 0); } while (a != 0) 215 216
Calculator with break Calculator with break Version without break evaluates a twice and requires an additional Equivalent and yet more simple: block. int a; int a = 1; int s = 0; int s = 0; for (;;) { for (;a != 0;) { std::cout << "next number =? "; std::cout << "next number =? "; std::cin >> a; std::cin >> a; if (a == 0) break; // stop loop in the middle if (a != 0) { s += a; s += a; std::cout << "sum = " << s << "\n"; std::cout << "sum = " << s << "\n"; } } } 217 218 continue -Statement Calculator with continue Ignore negative input: for (;;) continue; { std::cout << "next number =? "; std::cin >> a; Jump over the rest of the body of the enclosing iteration statement if (a < 0) continue; // jump to } Iteration statement is not left. if (a == 0) break; s += a; std::cout << "sum = " << s << "\n"; } 219 220
Equivalence of Iteration Statements Control Flow Order of the (repeated) execution of statements generally from top to bottom. . . We have seen: . . . except in selection and iteration statements while and do can be simulated with for condition It even holds: Not so simple if a continue is used! true The three iteration statements provide the same “expressiveness” if ( condition ) false statement (lecture notes) statement 221 222 Control Flow if else Control Flow for for ( init statement condition ; expression ) condition statement true false statement1 if ( condition ) init-statement statement1 else condition statement2 true statement2 statement false expression 223 224
Control Flow break in for Control Flow continue in for init-statement init-statement condition condition statement statement continue break expression expression 226 227 Control Flow while Control Flow do while condition true statement true false statement condition false 228 229
Control Flow: the Good old Times? BASIC and home computers... ...allowed a whole generation of young adults to program. Observation Actually, we only need if and jumps to if arbitrary places in the program ( goto ). Models: goto http://de.wikipedia.org/wiki/Commodore_64 Machine Language Assembler (“higher” machine language) BASIC, the first prorgamming language for the general public (1964) Home-Computer Commodore C64 (1982) 230 231 Spaghetti-Code with goto The “right” Iteration Statement Output of all prime numbers with BASIC Goals: readability, conciseness, in particular few statements true few lines of code simple control flow true simple expressions Often not all goals can be achieved simultaneously. 232 233
Odd Numbers in { 0 , . . . , 100 } Odd Numbers in { 0 , . . . , 100 } First (correct) attempt: Less statements, less lines: for (unsigned int i = 0; i < 100; ++i) for (unsigned int i = 0; i < 100; ++i) { { if (i % 2 == 0) if (i % 2 != 0) continue; std::cout << i << "\n"; std::cout << i << "\n"; } } 234 235 Odd Numbers in { 0 , . . . , 100 } Jump Statements Less statements, simpler control flow: for (unsigned int i = 1; i < 100; i += 2) implement unconditional jumps. std::cout << i << "\n"; are useful, such as while and do but not indispensible should be used with care: only where the control flow is simplified instead of making it more complicated This is the “right” iteration statement! 236 237
The switch -Statement Semantics of the switch -statement int Note; ... switch ( condition) switch (Note) { switch ( condition) statement case 6: statement std::cout << "super!"; condition is evaluated. break; If statement contains a case -label with (constant) value of condition : Expression, convertible to case 5: integral type std::cout << "cool!"; condition , then jump there break; otherwise jump to the default -lable, if available. If not, jump over statement : arbitrary statemet, in case 4: which case and default -lables are statement . std::cout << "ok."; permitted, break has a special break; The break statement ends the switch -statement. meaning. default: std::cout << "hmm..."; } 238 239 Control Flow switch Control Flow switch in general If break is missing, continue with the next case. 7: ??? switch (Note) { switch case 6: 6: ok. case 5: case case 4: 5: ok. std::cout << "ok."; break; 4: ok. case 1: break std::cout << "o"; case case 2: statement 3: oops! std::cout << "o"; case 3: 2: ooops! break std::cout << "oops!"; default break; 1: oooops! default: std::cout << "???"; 0: ??? } 240 241
“Proper Calculation” // Program: fahrenheit_float.cpp // Convert temperatures from Celsius to Fahrenheit. #include <iostream> 6. Floating-point Numbers I int main() { // Input std::cout << "Temperature in degrees Celsius =? "; float celsius; Types float and double ; Mixed Expressions and Conversion; std::cin >> celsius; Holes in the Value Range // Computation and output std::cout << celsius << " degrees Celsius are " << 9 * celsius / 5 + 32 << " degrees Fahrenheit.\n"; return 0; } 242 243 Fixed-point numbers Floating-point numbers fixed number of integer places (e.g. 7) fixed number of significant places (e.g. 10) fixed number of decimal places (e.g. 3) plus position of the decimal point 0.0824 = 0000000.082 third place truncated 82.4 = 824 · 10 − 1 Disadvantages 0.0824 = 824 · 10 − 4 Value range is getting even smaller than for integers. Representability depends on the position of the decimal point. Mantissa × 10 Exponent Number is 244 245
Types float and double Arithmetic Operators are the fundamental C++ types for floating point numbers Like with int , but . . . approximate the field of real numbers ( ❘ , + , × ) from mathematics Division operator / models a “proper” division (real-valued, not have a big value range, sufficient for many applications ( double integer) provides more places than float ) No modulo operators such as % or %= are fast on many computers 246 247 Literals Computing with float : Example are different from integers by providing decimal point 1.23e-7f Approximating the Euler-Number 1.0 : type double , value 1 ∞ � 1 e = i ! ≈ 2 . 71828 . . . integer part exponent 1.27f : type float , value 1 . 27 i =0 and / or exponent. fractional part using the first 10 terms. 1e3 : type double , value 1000 1.23e-7 : type double , value 1 . 23 · 10 − 7 1.23e-7f : type float , value 1 . 23 · 10 − 7 248 249
Computing with float : Euler Number Computing with float : Euler Number // Program: euler.cpp // Approximate the Euler number e. Value after term 1: 2 #include <iostream> Value after term 2: 2.5 int main () Value after term 3: 2.66667 { // values for term i, initialized for i = 0 Value after term 4: 2.70833 float t = 1.0f; // 1/i! float e = 1.0f; // i-th approximation of e Value after term 5: 2.71667 Value after term 6: 2.71806 std::cout << "Approximating the Euler number...\n"; // steps 1,...,n Value after term 7: 2.71825 for (unsigned int i = 1; i < 10; ++i) { t /= i; // 1/(i-1)! -> 1/i! Value after term 8: 2.71828 e += t; std::cout << "Value after term " << i << ": " << e << "\n"; Value after term 9: 2.71828 } return 0; } 250 251 Mixed Expressions, Conversion Value range Integer Types: Floating point numbers are more general than integers. Over- and Underflow relatively frequent, but ... In mixed expressions integers are converted to floating point numbers. the value range is contiguous (no “holes”): ❩ is “discrete”. Floating point types: 9 * celsius / 5 + 32 Overflow and Underflow seldom, but ... there are holes: ❘ is “continuous”. 252 253
Holes in the value range float n1; input 1.1 std::cout << "First number =? "; What is going on here? std::cin >> n1; float n2; 7. Floating-point Numbers II input 1.0 std::cout << "Second number =? "; std::cin >> n2; Floating-point Number Systems; IEEE Standard; Limits of float d; input 0.1 Floating-point Arithmetics; Floating-point Guidelines; Harmonic std::cout << "Their difference =? "; std::cin >> d; Numbers std::cout << "Computed difference − input difference = " << n1 − n2 − d << "\n"; output 2.23517e-8 254 255 Floating-point Number Systems Floating-point number Systems F ( β, p, e min , e max ) contains the numbers A Floating-point number system is defined by the four natural numbers: p − 1 � d i β − i · β e , β ≥ 2 , the base, ± p ≥ 1 , the precision (number of places), i =0 e min , the smallest possible exponent, d i ∈ { 0 , . . . , β − 1 } , e ∈ { e min , . . . , e max } . e max , the largest possible exponent. represented in base β : Notation: ± d 0 • d 1 . . . d p − 1 × β e , F ( β, p, e min , e max ) 256 257
Floating-point Number Systems Normalized representation Normalized number: Example ± d 0 • d 1 . . . d p − 1 × β e , d 0 � = 0 β = 10 Representations of the decimal number 0.1 Remark 1 The normalized representation is unique and therefore prefered. 1 . 0 · 10 − 1 , 0 . 1 · 10 0 , 0 . 01 · 10 1 , . . . Remark 2 The number 0 (and all numbers smaller than β e min ) have no normalized representation (we will deal with this later)! 258 259 Set of Normalized Numbers Normalized Representation Example F ∗ (2 , 3 , − 2 , 2) (only positive numbers) e = − 2 e = − 1 d 0 • d 1 d 2 e = 0 e = 1 e = 2 1 . 00 2 0 . 25 0 . 5 1 2 4 1 . 01 2 0 . 3125 0 . 625 1 . 25 2 . 5 5 F ∗ ( β, p, e min , e max ) 1 . 10 2 0 . 375 0 . 75 1 . 5 3 6 1 . 11 2 0 . 4375 0 . 875 1 . 75 3 . 5 7 0 8 1 . 00 · 2 − 2 = 1 1 . 11 · 2 2 = 7 4 260 261
Conversion Decimal → Binary Binary and Decimal Systems Assume, 0 < x < 2 . Binary representation: Internally the computer computes with β = 2 0 � b i 2 i = b 0 • b − 1 b − 2 b − 3 . . . (binary system) x = Literals and inputs have β = 10 i = −∞ − 1 (decimal system) � � 0 b i 2 i = b 0 + b i − 1 2 i − 1 = b 0 + Inputs have to be converted! i = −∞ i = −∞ � � � 0 b i − 1 2 i = b 0 + / 2 i = −∞ � �� � x ′ = b − 1 • b − 2 b − 3 b − 4 262 265 Conversion Decimal → Binary Binary representation of 1 . 1 Assume 0 < x < 2 . x b i x − b i 2( x − b i ) Hence: x ′ = b − 1 • b − 2 b − 3 b − 4 . . . = 2 · ( x − b 0 ) 1 . 1 b 0 = 1 0 . 1 0 . 2 0 . 2 b − 1 = 0 0 . 2 0 . 4 Step 1 (for x ): Compute b 0 : 0 . 4 b − 2 = 0 0 . 4 0 . 8 � 1 , if x ≥ 1 0 . 8 b − 3 = 0 0 . 8 1 . 6 b 0 = 0 , otherwise 1 . 6 b − 4 = 1 0 . 6 1 . 2 1 . 2 b − 5 = 1 0 . 2 0 . 4 Step 2 (for x ): Compute b − 1 , b − 2 , . . . : Go to step 1 (for x ′ = 2 · ( x − b 0 ) ) ⇒ 1 . 00011 , periodic, not finite 266 267
Binary Number Representations of 1 . 1 and 0 . 1 Binary Number Representations of 1 . 1 and 0 . 1 are not finite, hence there are errors when converting into a (finite) on my computer: binary floating-point system. = 1 . 1000000000000000888178 . . . 1.1 1.1f and 0.1f do not equal 1 . 1 and 0 . 1 , but are slightly inaccurate approximation of these numbers. = 1 . 1000000238418 . . . 1.1f In diff.cpp : 1 . 1 − 1 . 0 � = 0 . 1 268 269 The Excel-2007-Bug Computing with Floating-point Numbers std::cout << 850 ∗ 77.1; // 65535 Example ( β = 2 , p = 4 ): http://www.lomont.org/Math/Papers/2007/Excel2007/Excel2007Bug.pdf 1 . 111 · 2 − 2 1 . 011 · 2 − 1 + = 1 . 001 · 2 0 77 . 1 does not have a finite binary representation, we obtain 65534 . 9999999999927 . . . 1. adjust exponents by denormalizing one number 2. binary addition of the For this and exactly 11 other “rare” numbers the output (and only significands 3. renormalize 4. round to p significant places, if necessary the output) was wrong. 270 271
The IEEE Standard 754 The IEEE Standard 754 defines floating-point number systems and their rounding behavior Why F ∗ (2 , 24 , − 126 , 127)? is used nearly everywhere Single precision ( float ) numbers: F ∗ (2 , 24 , − 126 , 127) 1 sign bit plus 0 , ∞ , . . . 23 bit for the significand (leading bit is 1 and is not stored) Double precision ( double ) numbers: 8 bit for the exponent (256 possible values)(254 possible F ∗ (2 , 53 , − 1022 , 1023) plus 0 , ∞ , . . . exponents, 2 special values: 0 , ∞ ,. . . ) All arithmetic operations round the exact result to the next ⇒ 32 bit in total. representable number 272 273 The IEEE Standard 754 Floating-point Rules Rule 1 Why F ∗ (2 , 53 , − 1022 , 1023)? Rule 1 Do not test rounded floating-point numbers for equality. 1 sign bit 52 bit for the significand (leading bit is 1 and is not stored) for (float i = 0.1; i != 1.0; i += 0.1) 11 bit for the exponent (2046 possible exponents, 2 special std::cout << i << "\n"; values: 0 , ∞ ,. . . ) endless loop because i never becomes exactly 1 ⇒ 64 bit in total. 274 275
Floating-point Rules Rule 2 Harmonic Numbers Rule 2 Rule 2 Do not add two numbers of very different orders of magnitude! The n -the harmonic number is � n 1 i ≈ ln n. H n = 1 . 000 · 2 5 i =1 +1 . 000 · 2 0 = 1 . 00001 · 2 5 This sum can be computed in forward or backward direction, which is mathematically clearly equivalent “=” 1 . 000 · 2 5 (Rounding on 4 places) Addition of 1 does not have any effect! 276 277 Harmonic Numbers Rule 2 Harmonic Numbers Rule 2 // Program: harmonic.cpp // Compute the n-th harmonic number in two ways. Results: #include <iostream> int main() { // Input Compute H_n for n =? 10000000 std::cout << "Compute H_n for n =? "; unsigned int n; Forward sum = 15.4037 std::cin >> n; // Forward sum Backward sum = 16.686 float fs = 0; for (unsigned int i = 1; i <= n; ++i) fs += 1.0f / i; // Backward sum Compute H_n for n =? 100000000 float bs = 0; for (unsigned int i = n; i >= 1; --i) bs += 1.0f / i; Forward sum = 15.4037 // Output Backward sum = 18.8079 std::cout << "Forward sum = " << fs << "\n" << "Backward sum = " << bs << "\n"; return 0; } 278 279
Harmonic Numbers Rule 2 Floating-point Guidelines Rule 3 Observation: The forward sum stops growing at some point and is “really” wrong. Rule 4 The backward sum approximates H n well. Do not subtract two numbers with a very similar value. Explanation: Cancellation problems, cf. lecture notes. For 1 + 1 / 2 + 1 / 3 + · · · , later terms are too small to actually contribute Problem similar to 2 5 + 1 “=” 2 5 280 281 Literature David Goldberg: What Every Computer Scientist Should Know About Floating-Point Arithmetic (1991) 8. Functions I Defining and Calling Functions, Evaluation of Function Calls, the Type void , Pre- and Post-Conditions Randy Glasbergen, 1996 282 283
Functions Example: Computing Powers double a; int n; std::cin >> a; // Eingabe a std::cin >> n; // Eingabe n encapsulate functionality that is frequently used (e.g. computing powers) and make it easily accessible double result = 1.0; "Funktion pow " structure a program: partitioning into small sub-tasks, each of if (n < 0) { // a^n = (1/a)^( − n) a = 1.0/a; which is implemented as a function n = − n; } ⇒ Procedural programming; procedure: a different word for function. for (int i = 0; i < n; ++i) result ∗ = a; std::cout << a << "^" << n << " = " << ✭✭✭✭ resultpow(a,n) << ".\n"; 284 285 Function to Compute Powers Function to Compute Powers // Prog: callpow.cpp // PRE: e >= 0 || b != 0.0 // Define and call a function for computing powers. // POST: return value is b^e #include <iostream> double pow(double b, int e) { double pow(double b, int e){...} double result = 1.0; if (e < 0) { // b^e = (1/b)^( − e) b = 1.0/b; int main() e = − e; { } std::cout << pow( 2.0, − 2) << "\n"; // outputs 0.25 for (int i = 0; i < e; ++i) std::cout << pow( 1.5, 2) << "\n"; // outputs 2.25 result ∗ = b; std::cout << pow( − 2.0, 9) << "\n"; // outputs − 512 return result; } return 0; } 286 287
Function Definitions Defining Functions may not occur locally , i.e. not in blocks, not in other functions and not within control statements return type argument types can be written consecutively without separator in a program T fname ( T 1 pname 1 , T 2 pname 2 , . . . , T N pname N ) double pow (double b, int e) block { ... } body int main () function name formal arguments { ... } 288 289 Example: Xor Example: Harmonic // PRE: n >= 0 // POST: returns nth harmonic number // post: returns l XOR r // computed with backward sum bool Xor(bool l, bool r) float Harmonic(int n) { { return l && !r || !l && r; float res = 0; for (unsigned int i = n; i >= 1; −− i) } res += 1.0f / i; return res; } 290 291
Example: min Function Calls fname ( expression 1 , expression 2 , . . . , expression N ) // POST: returns the minimum of a and b int min(int a, int b) All call arguments must be convertible to the respective formal { argument types. if (a<b) return a; The function call is an expression of the return type of the else function. Value and effect as given in the postcondition of the return b; function fname . } Example: pow(a,n) : Expression of type double 292 293 Function Calls Evaluation of a Function Call Evaluation of the call arguments For the types we know up to this point it holds that: Initialization of the formal arguments with the resulting values Execution of the function body: formal arguments behave laike Call arguments are R-values local variables The function call is an R-value. Execution ends with fname: R-value × R-value × · · · × R-value − → R-value return expression ; Return value yiels the value of the function call. 294 295
Example: Evaluation Function Call Formal arguments double pow(double b, int e){ assert (e >= 0 || b != 0); double result = 1.0; Declarative region: function definition if (e<0) { // b^e = (1/b)^( − e) are invisible outside the function definition b = 1.0/b; are allocated for each call of the function (automatic storage Call of pow e = − e; duration) } for (int i = 0; i < e ; ++i) modifications of their value do not have an effect to the values of result ∗ = b; the call arguments (call arguments are R-values) return result; } Return ... pow (2.0, − 2) 296 297 The type void Scope of Formal Arguments Fundamental type with empty value range double pow(double b, int e){ int main(){ Usage as a return type for functions that do only provide an effect double r = 1.0; double b = 2.0; if (e<0) { int e = − 2; b = 1.0/b; double z = pow(b, e); // POST: "(i, j)" has been written to e = − e; // standard output } std::cout << z; // 0.25 void print_pair (int i, int j) { for (int i = 0; i < e ; ++i) std::cout << b; // 2 std::cout << "(" << i << ", " << j << ")\n"; r ∗ = b; std::cout << e; // − 2 } return r; return 0; int main() } } { print_pair(3,4); // outputs (3, 4) Not the formal arguments b and e of pow but the variables return 0; } defined here locally in the body of main 298 299
void -Functions Pre- and Postconditions characterize (as complete as possible) what a function does do not require return . document the function for users and programmers (we or other execution ends when the end of the function body is reached or if people) return; is reached make programs more readable: we do not have to understand or how the function works return expression ; is reached. are ignored by the compiler Pre and postconditions render statements about the correctness of a program possible – provided they are correct. Expression with type void (e.g. a call of a function with return type void 300 301 Preconditions Postconditions postcondition: precondition: What is guaranteed to hold after the function call? what is required to hold when the function is called? Specifies value and effect of the function call. defines the domain of the function 0 e is undefined for e < 0 Here only value, no effect. // PRE: e >= 0 || b != 0.0 // POST: return value is b^e 302 303
Pre- and Postconditions Pre- and Postconditions should be correct: We do not make a statement about what happens if the precondition does not hold. if the precondition holds when the function is called then also the postcondition holds after the call. C++ -standard-slang: „Undefined behavior”. Funktion pow : works for all numbers b � = 0 Function pow : division by 0 304 305 Pre- and Postconditions White Lies... // PRE: e >= 0 || b != 0.0 pre-condition should be as weak as possible (largest possible // POST: return value is b^e domain) post-condition should be as strong as possible (most detailed is formally incorrect: information) Overflow if e or b are too large b e potentially not representable as a double (holes in the value range!) 306 307
White Lies are Allowed Checking Preconditions... // PRE: e >= 0 || b != 0.0 Preconditions are only comments. // POST: return value is b^e How can we ensure that they hold when the function is called? The exact pre- and postconditions are platform-dependent and often complicated. We abstract away and provide the mathematical conditions. ⇒ compromise between formal correctness and lax practice. 308 309 ...with assertions Postconditions with Asserts The result of “complex” computations is often easy to check. Then the use of asserts for the postcondition is worthwhile. #include <cassert> ... // PRE: e >= 0 || b != 0.0 // PRE: the discriminant p ∗ p/4 − q is nonnegative // POST: return value is b^e // POST: returns larger root of the polynomial x^2 + p x + q double pow(double b, int e) { double root(double p, double q) assert (e >= 0 || b != 0); { double result = 1.0; assert(p ∗ p/4 >= q); // precondition ... double x1 = − p/2 + sqrt(p ∗ p/4 − q); } assert(equals(x1 ∗ x1+p ∗ x1+q,0)); // postcondition return x1; } 310 311
Exceptions Assertions are a rough tool; if an assertions fails, the program is halted in a unrecoverable way. C++ provides more elegant means (exceptions) in order to deal 9. Functions II with such failures depending on the situation and potentially without halting the program Failsafe programs should only halt in emergency situations and Stepwise Refinement, Scope, Libraries and Standard Functions therefore should work with exceptions. For this course, however, this goes too far. 312 313 Stepwise Refinement Stepwise Refinement Niklaus Wirth. Program development by stepwise refinement. Commun. ACM 14, 4, 1971 Solve the problem step by step. Start with a coarse solution on a high level of abstraction (only comments and abstract function calls) At each step, comments are replaced by program text, and functions are implemented (using the same principle again) A simple technique to solve complex problems The refinement also refers to the development of data representation (more about this later). If the refinement is realized as far as possible by functions, then partial solutions emerge that might be used for other problems. Stepwise refinement supports (but does not replace) the structural understanding of a problem. 314 315
Example Problem Coarse Solution (include directives omitted) Find out if two rectangles intersect! int main() { // input rectangles // intersection? // output solution return 0; } 316 318 Refinement 1: Input Rectangles Refinement 1: Input Rectangles Width w and height h may be negative. ( x 1 , y 1 , w 1 , h 1 ) h 1 ( x 1 , y 1 ) ( x, y, w, h ) y w 1 h ≥ 0 ( x 2 , y 2 , w 2 , h 2 ) h 2 ( x 2 , y 2 ) ( x, y ) w 2 w < 0 x 319 320
Refinement 1: Input Rectangles Refinement 2: Intersection? and Output int main() int main() { { std::cout << "Enter two rectangles [x y w h each] \n"; input rectangles � int x1, y1, w1, h1; std::cin >> x1 >> y1 >> w1 >> h1; bool clash = rectangles_intersect (x1,y1,w1,h1,x2,y2,w2,h2); int x2, y2, w2, h2; std::cin >> x2 >> y2 >> w2 >> h2; if (clash) // intersection? std::cout << "intersection!\n"; else std::cout << "no intersection!\n"; // output solution return 0; return 0; } } 321 322 Refinement 3: Intersection Function... Refinement 3: Intersection Function... bool rectangles_intersect (int x1, int y1, int w1, int h1, bool rectangles_intersect (int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) int x2, int y2, int w2, int h2) { { return false; // todo return false; // todo } } int main() { Function main � input rectangles � � intersection? output solution � return 0; } 323 324
Refinement 3: ...with PRE and POST Refinement 4: Interval Intersection Two rectangles intersect if and only if their x and y -intervals // PRE: (x1, y1, w1, h1), (x2, y2, w2, h2) are rectangles, // where w1, h1, w2, h2 may be negative. intersect. // POST: returns true if (x1, y1, w1, h1) and // (x2, y2, w2, h2) intersect bool rectangles_intersect (int x1, int y1, int w1, int h1, h 1 int x2, int y2, int w2, int h2) [ y 1 , y 1 + h 1 ] { return false; // todo ( x 1 , y 1 ) w 1 h 2 } [ y 2 , y 2 + h 2 ] ( x 2 , y 2 ) w 2 [ x 1 , x 1 + w 1 ] [ x 2 , x 2 + w 2 ] 325 326 Refinement 4: Interval Intersections Refinement 4: Interval Intersections // PRE: (x1, y1, w1, h1), (x2, y2, w2, h2) are rectangles, where // PRE: [a1, b1], [a2, b2] are (generalized) intervals, // w1, h1, w2, h2 may be negative. // with [a,b] := [b,a] if a>b // POST: returns true if (x1, y1, w1, h1),(x2, y2, w2, h2) intersect // POST: returns true if [a1, b1],[a2, b2] intersect bool rectangles_intersect (int x1, int y1, int w1, int h1, bool intervals_intersect (int a1, int b1, int a2, int b2) int x2, int y2, int w2, int h2) { { return false; // todo return intervals_intersect (x1, x1 + w1, x2, x2 + w2) } && intervals_intersect (y1, y1 + h1, y2, y2 + h2); � } Function rectangles_intersect � Function main � 327 328
Refinement 5: Min and Max Refinement 5: Min and Max // POST: the maximum of x and y is returned // PRE: [a1, b1], [a2, b2] are (generalized) intervals, int max (int x, int y){ // with [a,b] := [b,a] if a>b if (x>y) return x; else return y; // POST: returns true if [a1, b1],[a2, b2] intersect } bool intervals_intersect (int a1, int b1, int a2, int b2) already exists in the standard library { // POST: the minimum of x and y is returned return max(a1, b1) >= min(a2, b2) int min (int x, int y){ && min(a1, b1) <= max(a2, b2); � if (x<y) return x; else return y; } } Function intervals_intersect � Function rectangles_intersect � Function main � 329 330 Back to Intervals Look what we have achieved step by step! // PRE: [a1, b1], [a2, h2] are (generalized) intervals, // with [a,b] := [b,a] if a>b #include<iostream> int main () // POST: returns true if [a1, b1],[a2, b2] intersect #include<algorithm> { std::cout << "Enter two rectangles [x y w h each]\n"; bool intervals_intersect (int a1, int b1, int a2, int b2) // PRE: [a1, b1], [a2, h2] are (generalized) intervals, int x1, y1, w1, h1; // with [a,b] := [b,a] if a>b std::cin >> x1 >> y1 >> w1 >> h1; { // POST: returns true if [a1, b1],[a2, b2] intersect int x2, y2, w2, h2; bool intervals_intersect (int a1, int b1, int a2, int b2) std::cin >> x2 >> y2 >> w2 >> h2; return std::max(a1, b1) >= std::min(a2, b2) { bool clash = rectangles_intersect (x1,y1,w1,h1,x2,y2,w2,h2); return std::max(a1, b1) >= std::min(a2, b2) if (clash) && std::min(a1, b1) <= std::max(a2, b2); � && std::min(a1, b1) <= std::max(a2, b2); std::cout << "intersection!\n"; } else } std::cout << "no intersection!\n"; // PRE: (x1, y1, w1, h1), (x2, y2, w2, h2) are rectangles, where return 0; // w1, h1, w2, h2 may be negative. } // POST: returns true if (x1, y1, w1, h1),(x2, y2, w2, h2) intersect bool rectangles_intersect (int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { return intervals_intersect (x1, x1 + w1, x2, x2 + w2) && intervals_intersect (y1, y1 + h1, y2, y2 + h2); } 331 332
Result Where can a Function be Used? Clean solution of the problem #include<iostream> Useful functions have been implemented int main() intervals_intersect { rectangles_intersect std::cout << f(1); // Error: f undeclared return 0; } int f (int i) // Scope of f starts here { Gültigkeit f return i; Intersection } 333 334 Scope of a Function This does not work... #include<iostream> is the part of the program where a function can be called is defined as the union of all scopes of its declarations (there can int main() { be more than one) std::cout << f(1); // Error: f undeclared return 0; declaration of a function: like the definition but without {...} . } int f (int i) // Scope of f starts here double pow (double b, int e); { Gültigkeit f return i; } 335 336
...but this works! Forward Declarations, why? Functions that mutually call each other: #include<iostream> int f (int i); // Gueltigkeitsbereich von f ab hier int g(...); // forward declaration int main() { int f (...) // f valid from here std::cout << f(1); { return 0; g(...) // ok Gültigkeit g } } Gültigkeit f int f (int i) int g (...) { { f(...) // ok return i; } } 337 338 Reusability Level 1: Outsource the Function // PRE: e >= 0 || b != 0.0 // POST: return value is b^e double pow(double b, int e) Functions such as rectanges and pow are useful in many { programs. double result = 1.0; “Solution”: copy-and-paste the source code if (e < 0) { // b^e = (1/b)^( − e) b = 1.0/b; Main disadvantage: when the function definition needs to be e = − e; adapted, we have to change all programs that make use of the } function for (int i = 0; i < e; ++i) result ∗ = b; return result; } 339 340
Level 1: Include the Function Disadvantage of Including // Prog: callpow2.cpp // Call a function for computing powers. #include <iostream> #include copies the file ( math.cpp ) into the main program file in working directory #include "math.cpp" ( callpow2.cpp ). int main() The compiler has to (re)compile the function definition for each { program std::cout << pow( 2.0, − 2) << "\n"; This can take long for many and large functions. std::cout << pow( 1.5, 2) << "\n"; std::cout << pow( 5.0, 1) << "\n"; std::cout << pow( − 2.0, 9) << "\n"; return 0; } 341 342 Level 2: Separate Compilation Level 2: Separate Compilation of math.cpp independent of the main program: Declaration of all used symbols in so-called header file. 001110101100101010 double pow(double b, // PRE: e >= 0 || b != 0.0 000101110101000111 int e) // POST: return value is b^e Funktion pow 000101000010111111 { double pow (double b, int e); g++ -c math.cpp ... 111100001101010001 } 111111101000111010 010101101011010001 100101111100101010 math.cpp math.o math.h 343 344
Level 2: Separate Compilation The linker unites... of the main program, independent of math.cpp , if a declaration of math is included. 001110101100101010 001110101100101010 000101110101000111 000101110101000111 001110101100101010 Funktion pow 000101000010111111 000101000010111111 Funktion main + #include <iostream> 000101110101000111 111100001101010001 111100001101010001 #include "math.h" Funktion main 000101000010111111 int main() 111111101000111010 010101101011010001 { 111100001101010001 rufe "pow" auf! 010101101011010001 100101111100101010 std::cout << pow(2, − 2) << "\n"; 010101101011010001 100101111100101010 111111101000111010 return 0; rufe "pow" auf! 100101111100101010 math.o callpow3.o } 111111101000111010 callpow3.cpp callpow3.o 345 346 ... what belongs together Availability of Source Code? 001110101100101010 000101110101000111 Funktion pow 000101000010111111 Observation 111100001101010001 001110101100101010 001110101100101010 111111101000111010 math.cpp (source code) is not required any more when the math.o 000101110101000111 000101110101000111 010101101011010001 Funktion pow (object code) is available. Funktion main 000101000010111111 000101000010111111 = + 100101111100101010 111100001101010001 111100001101010001 001110101100101010 111111101000111010 010101101011010001 Many vendors of libraries do not provide source code. 000101110101000111 rufe "pow" auf! 010101101011010001 100101111100101010 000101000010111111 Funktion main Header files then provide the only readable informations. 100101111100101010 111111101000111010 111100001101010001 math.o callpow3.o 010101101011010001 rufe addr auf! 100101111100101010 111111101000111010 Executable callpow 347 348
,,Open Source” Software Libraries Source code is generally available. Logical grouping of similar functions Only this allows the continued development of code by users and dedicated “hackers”. pow Even in commercial domains, “open source” gains ground. exp math.h Certain licenses force naming sources and open development. Example GPL log math.cpp (GNU Genereal Public License) Known open source software: Linux (operating system), Firefox (browser), sin Thunderbird (email program)... 349 350 Name Spaces... ...Avoid Name Conflicts // ifeemath.h // A small library of mathematical functions #include <cmath> namespace ifee { #include "ifeemath.h" // PRE: e >= 0 || b != 0.0 int main() // POST: return value is b^e { double pow (double b, int e); double x = std::pow (2.0, − 2); // <cmath> double y = ifee::pow (2.0, − 2); // ifeemath.h .... } double exp (double x); ... } 351 352
Functions from the Standard Library Name Spaces / Compilation Units In C++ the concept of separate compilation is independent of the help to avoid re-inventing the wheel (such as with ifee::pow ); concept of name spaces lead to interesting and efficient programs in a simple way; In some other languages,e.g. Modula / Oberon (partially also for guarantee a quality standard that cannot easily be achieved with Java) the compilation unit can define a name space. code written from scratch. 353 354 Prime Number Test with sqrt Prime Number test with sqrt n ≥ 2 is a prime number if and only if there is no d in { 2 , . . . , ⌊√ n ⌋} n ≥ 2 is a prime number if and only if there is no d in { 2 , . . . , n − 1 } dividing n . dividing n . unsigned int d; unsigned int bound = std::sqrt(n); for (d=2; n % d != 0; ++d); unsigned int d; for (d = 2; d <= bound && n % d != 0; ++d); This works because std::sqrt rounds to the next representable double number (IEEE Standard 754). Other mathematical functions ( std::pow ,. . . ) are almost as exact in practice. 355 356
Prime Number test with sqrt Functions Should be More Capable! Swap ? // Test if a given natural number is prime. #include <iostream> #include <cassert> #include <cmath> void swap (int x, int y) { int main () int t = x; { // Input unsigned int n; x = y; std::cout << "Test if n>1 is prime for n =? "; std::cin >> n; y = t; assert (n > 1); } // Computation: test possible divisors d up to sqrt(n) unsigned int bound = std::sqrt(n); unsigned int d; int main(){ for (d = 2; d <= bound && n % d != 0; ++d);@ int a = 2; // Output if (d <= bound) // d is a divisor of n in {2,...,[sqrt(n)]} int b = 1; std::cout << n << " = " << d << " ∗ " << n / d << ".\n"; else swap (a, b); // no proper divisor found std::cout << n << " is prime.\n"; assert (a==1 && b==2); // fail! return 0; } } 357 358 Functions Should be More Capable! Swap ? Sneak Preview: Reference Types // POST: values of x and y are exchanged void swap (int& x, int& y) { int t = x; x = y; We can enable functions to change the value of call arguments. y = t; Not a new concept for functions but rather a new class of types } int main(){ int a = 2; Ref int b = 1; swap (a, b); assert (a==1 && b==2); // ok! } 359 360
Swap! // POST: values of x and y are exchanged void swap (int& x, int& y) { int t = x; x = y; 10. Reference Types y = t; } int main(){ Reference Types: Definition and Initialization, Call By Value, Call by int a = 2; Reference, Temporary Objects, Constants, Const-References int b = 1; swap (a, b); assert (a == 1 && b == 2); // ok! } 361 362 Reference Types Reference Types: Definition read as „ T -reference” T & We can make functions change the values of the call arguments no new concept for functions, but a new class of types underlying type T & has the same range of values and functionality as T , ... Reference Types but initialization and assignment work differently. 363 364
Anakin Skywalker alias Darth Vader Anakin Skywalker alias Darth Vader int anakin_skywalker = 9; int& darth_vader = anakin_skywalker; // alias int& lord_vader = darth_vader; // another alias darth_vader = 22; assignment to the L-value behind the alias std::cout << anakin_skywalker; // 22 anakin_skywalker anakin_skywalker darth_vader darth_vader lord_vader 22 365 366 Reference Types: Intialization and Assignment Reference Types: Implementation int& darth_vader = anakin_skywalker; Internally, a value of type T & is represented by the address of an darth_vader = 22; // anakin_skywalker = 22 object of type T . A variable of reference type (a reference ) can only be initialized int& j; // Error: j must be an alias of something with an L-Value . int& k = 5; // Error: the literal 5 has no address The variable is becoming an alias of the L-value (a different name for the referenced object). Assignment to the reference is to the object behind the alias. 367 368
Call by Reference Call by Reference Reference types make it possible that functions modify the value of the call arguments: void increment (int& i) initialization of the formal arguments { // i becomes an alias of the call argument Formal argument has reference type: ++i; } ⇒ Call by Reference ... int j = 5; increment (j); Formal argument is (internally) initialized with the address of the call std::cout << j << "\n"; // 6 argument (L-value) and thus becomes an alias . j i 6 369 370 Call by Value In Context: Assignment to References // PRE: [a1, b1], [a2, b2] are (generalized) intervals, // POST: returns true if [a1, b1], [a2, b2] intersect, in which case // [l, h] contains the intersection of [a1, b1], [a2, b2] Formal argument does not have a reference type: bool intervals_intersect (int& l, int& h, int a1, int b1, int a2, int b2) { ⇒ Call by Value sort (a1, b1); a 1 b 1 sort (a2, b2); l = std::max (a1, a2); a 2 h = std::min (b1, b2); b 2 Formal argument is initialized with the value of the actual parameter return l <= h; (R-Value) and thus becomes a copy . } ... int lo = 0; int hi = 0; if (intervals_intersect (lo, hi, 0, 2, 1, 3)) std::cout << "[" << lo << "," << hi << "]" << "\n"; // [1,2] 371 372
In Context: Initialization of References Return by Value / Reference // POST: a <= b void sort (int& a, int& b) { Even the return type of a function can be a reference type (return if (a > b) by reference) std::swap (a, b); // ’passing through’ of references a,b } In this case the function call itself is an L-value bool intervals_intersect (int& l, int& h, int a1, int b1, int a2, int b2) { int& increment (int& i) sort (a1, b1); // generates references to a1,b1 { sort (a2, b2); // generates references to a2,b2 return ++i; l = std::max (a1, a2); } h = std::min (b1, b2); exactly the semantics of the pre-increment return l <= h; } 373 374 Temporary Objects The Reference Guidline What is wrong here? int& foo (int i) Reference Guideline Return value of type int& be- { When a reference is created, the object referred to must “stay alive” comes an alias of the formal argu- return i; at least as long as the reference. ment. But the memory lifetime of i } ends after the call! int k = 3; int& j = foo (k); // j is an alias of a zombie std::cout << j << "\n"; // undefined behavior 375 376
The Compiler as Your Friend: Constants The Compiler as Your Friend: Constants Compiler checks that the const -promise is kept Constants const int speed_of_light = 299792458; ... are variables with immutable value speed_of_light = 300000000; const int speed_of_light = 299792458; Usage: const before the definition compiler: error Tool to avoid errors: constants guarantee the promise : “value does not change” 377 378 Constants: Variables behind Glass The const -guideline const -guideline For each variable , think about whether it will change its value in the lifetime of a program. If not, use the keyword const in order to make the variable a constant. A program that adheres to this guideline is called const -correct. 379 380
Const-References What exactly does Constant Mean? Consider an L-value with type const T have type const T & (= const ( T & )) can be initialized with R-Values (compiler generates a temporary Case 1: T is no reference type object with sufficient lifetime) Then the L-value is a constant. const T & r = lvalue ; const int n = 5; r is initialized with the address of lvalue (efficient) int& i = n; // error: const-qualification is discarded i = 6; const T & r = rvalue ; r is initialized with the address of a temporary object with the value The compiler detects our attempt to cheat of the rvalue (flexible) 381 382 What exactly does Constant Mean? When const T & ? Consider L-value of type const T Case 2: T is reference type. Rule Argument type const T & (call by read-only reference) is used for Then the L-value is a read-only alias which cannot be used to change the value efficiency reasons instead of T (call by value), if the type T requires large memory. For fundamental types ( int , double ,...) it does not int n = 5; pay off. const int& i = n;// i: read-only alias of n int& j = n; // j: read-write alias Examples will follow later in the course i = 6; // Error: i is a read-only alias j = 6; // ok: n takes on value 6 383 384
Array: Motivation Now we can iterate over numbers for (int i=0; i<n ; ++i) ... 11. Arrays I Often we have to iterate over data . (Example: find a cinema in Zurich that shows “ C++ Runner 2049” today) Array Types, Sieve of Erathostenes, Memory Layout, Iteration, Arrays allow to store homogeneous data (example: schedules of Vectors, Characters and Texts, ASCII, UTF-8, Caesar-Code all cinemas in Zurich) 385 386 Arrays: a first Application Sieve of Erathostenes: Initialization The Sieve of Erathostenes computes all prime numbers < n const unsigned int n = 1000; constant! method: cross out all non-prime numbers bool crossed_out[n]; for (unsigned int i = 0; i < n; ++i) 2 3 4 4 5 6 6 6 7 8 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 9 10 12 12 14 15 16 18 18 20 21 22 2 3 5 7 11 13 17 19 23 crossed_out[i] = false; at the end of the crossing out process, only prime numbers remain. crossed_out[i] indicates if i has been crossed out. Question: how do we cross out numbers ?? Answer: with an array . 387 388
Sieve of Eratosthenes: Computation Arrays: Definition for (unsigned int i = 2; i < n; ++i) Declaration of an array variable: if (!crossed_out[i] ){ T a [expr] // i is prime std::cout << i << " "; // cross out all proper multiples of i constant integer expression base type value provides the length of the array for (unsigned int m = 2 ∗ i; m < n; m += i) crossed_out[m] = true; variable of array type } type of a : „T[k]” value is known at compile time. } e.g. literal, constant values range of a : T k The sieve: go to the next non-crossed out number i (this must be a prime number), output the number and cross out all proper multiples Beispiel: bool crossed_out[n] of i 389 390 Memory Layout of an Array Random Access The L-value value i An array occupies a contiguous memory area a [ expr ] example: an array with 4 elements has type T and refers to the i -th element of the array a (counting from 0!) memory cells for a value of type T each a[0] a[1] a[2] a[3] 391 392
Random Access Random Access Random access is very efficient: p : address of a p + s · i : address of a[i] a [ expr ] The value i of expr is called array index . [] : subscript operator a[i] s: memory consumption of T (in cells) 393 394 Array Initialization Arrays are Primitive int a[5]; The five elements of a remain uninitialized (values can be Accessing elements outside the valid bounds of the array leads to assigned later) undefined behavior. int a[5] = {4, 3, 5, 2, 1}; int arr[10]; the 5 elements of a are initialized with an initialization list . for (int i=0; i<=10; ++i) int a[] = {4, 3, 5, 2, 1}; arr[i] = 30; // runtime error: access to arr[10]! also ok: the compiler will deduce the length 395 396
Arrays are Primitive Arrays are Primitive (II) Arrays cannot be initialized and assigned to like other types Array Bound Checks int a[5] = {4,3,5,2,1}; With no special compiler or runtime support it is the sole int b[5]; responsibility of the programmer to check the validity of element b = a; // Compiler error! accesses. int c[5] = a; // Compiler error! Why? 397 398 Arrays are Primitive Vectors Arrays are legacy from the language C and primitive from a Obvious disadvantage of static arrays: constant array length modern viewpoint const unsigned int n = 1000; In C, arrays are very low level and efficient, but do not offer any bool crossed_out[n]; luxury such as initialization or copying. remedy: use the type Vector from the standard library Missing array bound checks have far reaching consequences. #include <vector> Initialization with n elements Code with non-permitted but possible index accesses has been initial value false . ... exploited (far too) often for malware. std::vector<bool> crossed_out (n, false); the standard library offers comfortable alternatives element type in triangular brackets 399 400
Sieve of Erathostenes with Vectors Characters and Texts #include <iostream> #include <vector> // standard containers with array functionality int main() { We have seen texts before: // input std::cout << "Compute prime numbers in {2,...,n − 1} for n =? "; std::cout << "Prime numbers in {2,...,999}:\n"; unsigned int n; std::cin >> n; String-Literal // definition and initialization: provides us with Booleans // crossed_out[0],..., crossed_out[n − 1], initialized to false std::vector<bool> crossed_out (n, false); can we really work with texts? Yes: // computation and output std::cout << "Prime numbers in {2,...," << n − 1 << "}:\n"; for (unsigned int i = 2; i < n; ++i) Character: Value of the fundamental type char if (!crossed_out[i]) { // i is prime std::cout << i << " "; Text: Array with base type char // cross out all proper multiples of i for (unsigned int m = 2 ∗ i; m < n; m += i) crossed_out[m] = true; } std::cout << "\n"; return 0; 403 404 } The type char (“character”) The type char (“character”) is formally an integer type represents printable characters (e.g. ’a’ ) and control characters values convertible to int / unsigned int (e.g. ’ \ n’ ) all arithmetic operators are available (with dubious use: what is ’a’/’b’ ?) values typically occupy 8 Bit char c = ’a’ domain: {− 128 , . . . , 127 } or { 0 , . . . , 255 } defines variable c of type char with value ’a’ literal of type char 405 406
Recommend
More recommend