© Springer International Publishing AG, part of Springer Nature 2017
Joe Pitt-Francis and Jonathan WhiteleyGuide to Scientific Computing in C++Undergraduate Topics in Computer Sciencehttps://doi.org/10.1007/978-3-319-73132-2_2

2. Flow of Control

Joe Pitt-Francis1   and Jonathan Whiteley1
(1)
University of Oxford, Oxford, UK
 
 
Joe Pitt-Francis

In almost any computer program written for a scientific computing application, we need to allow the computer to execute a collection of statements if—and only if—some criterion is met. For example, if we were writing a program to control the motion of a spacecraft travelling to Mars, the program would include lines of code that would control the safe landing of the spacecraft. As the craft completes its touchdown, it fires retrorocket motors to control descent until the sensors detect that the landing gear is in contact with the planet’s surface. It is imperative that the lines of code which say “cut the motor if and only if there is a strong signal from the landing gear” are executed at exactly the right time. If these instructions are not executed when the spacecraft has landed, the retrorockets may fire for too long and cause damage to the craft. On the other hand, if the instruction to cut the motors is executed when the spacecraft is still descending, we would expect the spacecraft to crash.1 It is clear that the relevant lines of code should be executed if, and only if, certain conditions are met.

As with most programming languages, conditional branching may be achieved in C++ programs by using an if statement. Similarly, we may use a while statement to execute a collection of statements until a specified condition is met, and a for loop to execute a collection of statements a specified number of times. In this chapter, we explain how to utilise these features of the C++ language.

2.1 The if Statement

The most basic use of an if statement is to execute one or more statements if, and only if, a given condition is met. As we shall see in this section, we may build upon this simple construct to write more complicated statements when required.

2.1.1 A Single if Statement

Let us suppose that we wish to execute two statements, Statement1 and Statement2, if—and only if—the condition p > q is met. The following code demonstrates the basic syntax for this in C++.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figa_HTML.gif
If the condition p > q is met, then the code enclosed by the curly brackets is executed. The condition (in round brackets) is technically know as the guard . Note the indentation within the curly brackets in the above listing. While this is not necessary for the compiler to understand the meaning, it makes it clearer to the reader which statements are executed if the condition p > q is met.
If only one statement—Statement1—is to be executed when the condition p > q is satisfied, then curly brackets are not strictly necessary. For example, the following two code fragments will execute Statement1 if the condition p > q is met.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figb_HTML.gif
or
../images/218075_2_En_2_Chapter/218075_2_En_2_Figc_HTML.gif
Although either of these two variants of the code will do what we want it to, we do not recommend them, as the curly brackets make it very clear precisely which statements are executed as a consequence of a given if statement. As such, we would strongly suggest the use of curly brackets, as shown in the code below. More suggestions on tips for ensuring code is clearly readable—known as coding conventions—may be found in Sect. 6.​6.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figd_HTML.gif

2.1.2 Example: Code for a Single if Statement

Below is a concrete example of code that uses an if statement. This code changes the value of x to zero if, and only if, x is negative. If x is not negative, line 5 of the code will not be executed, and the value of x will be unchanged.
../images/218075_2_En_2_Chapter/218075_2_En_2_Fige_HTML.gif

2.1.3 ifelse Statements

It is often the case that we want to set a variable to one value if a specified condition is met, and to a different value otherwise. This may be implemented in C++ code by the use of an if statement in conjunction with an else statement. The fragment of code below sets the double precision floating point variable y to the value 2 if the integer variable i is positive, and to the value 10 otherwise.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figf_HTML.gif
Note the comment in line 10 of the listing above. As no condition is needed for the else condition, it is always good programming practice to use a comment to explicitly state under what conditions the else condition should be met.

2.1.4 Multiple if Statements

We may extend the if and else statements described above to allow more complicated conditions on the execution of statements. Extending the previous example, suppose the double precision floating point variable y takes the value 2 if the integer variable i is greater than 100, y takes the value 10 if i is negative, and y takes the value 5 otherwise. C++ code for this condition is given below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figg_HTML.gif

2.1.5 Nested if Statements

It is common in scientific computing to have an algorithm where statements must be executed if, and only if, two separate conditions are met. One way of implementing this is to use nested if statements, as shown below. In this code the double precision floating point variable y is assigned the value 10 if, and only if, the conditions xz and p > q are both met.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figh_HTML.gif

2.1.6 Boolean Variables

Boolean variables may be used as the condition with an if statement. This is demonstrated in the fragment of code below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figi_HTML.gif

2.2 Logical and Relational Operators

In Sect. 2.1 we demonstrated the use of if statements by using the relational operator “greater than”. To fully utilise if statements and, as we shall see later, while statements and for loops, we need to extend our range of logical and relational operators. These are summarised in Tables 2.1 and 2.2. The combination of logical and relational operators allow any reasonable condition to be implemented in C++ code.
Table 2.1

Logical operators in C++

Logical condition

Operator

AND

&&

OR

$$\mid \mid $$

NOT

!

Table 2.2

Relational operators in C++

Relation

Operator

Equal to

== (note that it is not “=”)

Not equal to

!=

Greater than

>

Less than

<

Greater than or equal to

>=

Less than or equal to

<=

A first example of the combination of logical and relational operators is to replace the nested if statements in Listing 2.1 by a single if statement. The condition in the new if statement is true if, and only if, both the condition x > z and the condition p > q are true. If this compound condition is met, the value 10 is assigned to the variable y. This is demonstrated in the code below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figj_HTML.gif
The example code fragment below uses a combination of logical and relational operators to set a double precision floating point variable y to the value 10 if either p > q or the integer variable i is not equal to 1. If neither of these conditions has been met, then the variable y is assigned the value −10.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figk_HTML.gif
The logical operator “NOT” is often used in conjunction with Boolean variables. This is demonstrated in the example code below, where the integer variable i is incremented by the value 2 if, and only if, the Boolean variable flag takes the value false.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figl_HTML.gif

2.3 The while Statement

A while statement is used if a collection of statements are to be executed until some prescribed condition is not met. The C++ syntax for while statements is similar to that for if statements.

A first example of a while statement is given below. A variable x is initially assigned the value 10. On each execution of the code inside the while statement the value of the variable x is halved. This is repeated while the value of the variable x is greater than 1.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figm_HTML.gif
Although while statements are frequently used in C++ programming, they should be used with care. Consider the fragment of code below. Suppose we want to develop Listing 2.2 above so that we count the number of times that we halve the variable x. This may be achieved by the use of an integer variable count which is incremented every time the statements inside the curly brackets are executed, as shown below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Fign_HTML.gif
The output of this code is shown below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figo_HTML.gif
The important thing to note in the example output above is that the condition x$$\,{&gt;}\,$$1.0 is tested only at the beginning of the statements enclosed within the curly brackets. In particular, this condition first became untrue when the variable x was assigned the value 0.625 at line 5 in the code. However, the condition x$$\,{&gt;}\,$$1.0 was not tested at this point, and so the variable count was incremented as line 8 will be executed before leaving the while loop.
Were we to want a loop to be executed at least once, regardless of any other conditions, then we can use the do-while syntax which tests at the end of the loop, as shown below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figp_HTML.gif
The output of this code (shown below) demonstrates that the body of the loop is executed once, even though the initial value of x does not satisfy the condition in the guard.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figq_HTML.gif
We may nest while statements in exactly the same way as if statements, described in Sect. 2.1.5.

2.4 Loops Using the for Statement

The simplest application of a for loop is to execute a collection of statements a specified number of times. The fragment of code below demonstrates how to execute a given statement 10 times.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figr_HTML.gif
Line 1 of the code above deserves more explanation. The first statement in this line of code declares an integer variable i, and initialises this variable to the value 0. The code inside the curly brackets is executed if, and only if, the variable i is less than 10. The final content of this line of code increments i by the value 1 each time all the statements enclosed by the curly brackets have been executed. The output of this code is therefore
../images/218075_2_En_2_Chapter/218075_2_En_2_Figs_HTML.gif
We may also nest for loops in a similar way to that for if statements described in Sect. 2.1.5. Furthermore, for loops may be defined to be executed a variable number of times, as demonstrated in the example code below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figt_HTML.gif
Before explaining what the code above does, it is important to understand what line 3 of code (the second for statement) does. In a similar vein to the discussion of the initial example of a for loop, we see that the first statement initialises the integer variable j to 5. The statements within the furthest indented curly brackets are executed when the variable j is greater than the variable i. Each time these statements have been executed, j is decremented by the value 1.
We are now in a position to understand the whole of the code above. The loop over the variable i is known as the outer loop, and the loop over the variable j is known as the inner loop. The first time the statements in the outer loop are executed, i takes the value 0. When i takes this value, the third line of code tells us that j takes the values 5, 4, 3, 2, 1. The second time the statements in the outer loop are executed, i will take the value 1, and so j will take the values 5, 4, 3, 2. We may now deduce that the output of the code above will be
../images/218075_2_En_2_Chapter/218075_2_En_2_Figu_HTML.gif

2.4.1 Example: Calculating the Scalar Product of Two Vectors

The scalar product between two vectors of the same length may be computed using a for loop. Suppose the vectors are both of length n, and are stored in double precision floating point arrays vector1 and vector2 of the correct size. Remembering that the indexing of C++ arrays begins from zero, the scalar product (discussed in more detail in Sect. A.​1.​2) between these vectors—defined to be a double precision floating point variable scalar_product—is given mathematically by the following sum:
$$\begin{aligned} \texttt {scalar\_product} = \sum \limits _{i = 0}^{n - 1} {\texttt {vector1}\,[\texttt {i}] \times \texttt {vector2}[\texttt {i}].} \end{aligned}$$
The mathematical expression above for calculating the scalar product is implemented in C++ below for the case n=2. Note that the variable scalar_product must be initialised to 0 before any calculation is carried out.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figv_HTML.gif

2.5 The switch Statement

A good understanding of the flow of control resulting from if, while and for statements is crucial for implementation of scientific computing applications. One further statement that is used less frequently is the switch statement. This statement is best explained by example. Consider the code below, where the variable i has been declared as an integer. Note that the language specification says that the control variable, which is i in our case, must be an integer and not a floating point type.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figw_HTML.gif
If i takes the value 1 when the code above is executed, the statements below line 4 will be executed until the line of code break is reached (line 8). At the point when break is reached, the flow of execution will leave the code inside the curly brackets. Similarly, if the code is executed when i takes the value 20, then the statements below line 6 will be executed until the line of code break is reached. For all other values of i the line of code after default (line 9) will be executed.

Switch statements were introduced to programming languages because they are very easy for compilers to implement efficiently. However, they are notorious as places where programmers introduce bugs by forgetting to end case statements with the break keyword or by forgetting to give a default case. Switch statements should be written with care.

2.6 Tips: Loops and Branches

In this tips section, we highlight several traps that programmers who are new to C++ may fall into.

2.6.1 Tip 1: A Common Novice Coding Error

Below is code that has been written with the intention of doubling a variable x five times.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figx_HTML.gif
It would be expected that this code would output the value $$2 \times 2^{5}=64$$. However, the actual output of this code is
../images/218075_2_En_2_Chapter/218075_2_En_2_Figy_HTML.gif
Why is this? Hint: look very closely at line 2 of the code above.

The reason for the surprising output is the semi-colon at the end of line 2. This is a common error for programmers who are new to the language. After seeing that most lines end with a semi-colon you might begin to get into the habit of ending every line with one. When you see the guard at the beginning of a for, while or if statement without a semi-colon at the end then it might be tempting to stick one in!

You might ask “If the loop is not executing as intended, why is the final answer x = 4 and not x = 2?”. The answer is that the empty space in line 2 between the “)” and the “;” is being interpreted as the body of the loop—it is the empty nothing which is executed 5 times. The intended body of the loop (lines 3–5) is treated as a block with special scope (see Sect. 5.​1 for more information). This block has no connection with the for loop and is executed once.

2.6.2 Tip 2: Counting from Zero

Programmers who are experienced with Matlab or Fortran may be used to a loop beginning from 1 and ending when the loop variable reaches a given value. If we wish a loop to execute exactly four times, we would write it in Matlab or Fortran as
../images/218075_2_En_2_Chapter/218075_2_En_2_Figz_HTML.gif
../images/218075_2_En_2_Chapter/218075_2_En_2_Figaa_HTML.gif
In both cases the variable j (in the Matlab code) or J (in the Fortran code) takes values from 1 to 4 inclusive. When programming in C++ it is common to write the equivalent loop from 0 up to, but not including, 4. That is, j = 0, 1, 2, 3. The reason for this is that while Matlab and Fortran use one-based indexing where array indexing starts at 1, C++ uses zero-based indexing . It is a good idea to write loops in the form of the second loop given below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figab_HTML.gif

2.6.3 Tip 3: Equality Versus Assignment

When we introduced relational operators in Table 2.2, we noted that there is a difference between a single = and a double ==. The operator = is an assignment operator which takes the value on the right-hand side and assigns it to the variable on the left-hand side. The equality operator == returns true if, and only if, the values on the left and right are equal.

A common programming error is to mistake one for the other.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figac_HTML.gif
The code above shows two common unintended bugs in C++ code. Line 2 of this code will test whether or not the variable x is equal to 4, but assign no value to x. This line therefore has no overall effect. Your compiler may give you a warning. However, as different compilers will give different warnings, you should not rely on this. Unless suitable compiler flags are used the compiler will give no error since it is valid syntax. The second error is shown in lines 8–11 of the code. In this case, line 8 of the code uses assignment (a single equals sign) when equality testing (a double equals sign) was intended. This code will have the effect of changing the value of x to the value 4 when this was not intended. The condition which is actually tested is obtained from the value of the assignment. A non-zero value (in this case the value 4) is interpreted as success, and so this condition is met. The code inside the curly brackets therefore will be executed, and so the variable x will take the value 6. Again, this is valid syntax so the compiler may give no warning or error.

Some compilers may report these types of problems as either warnings or errors. You may be able to ensure that the compiler informs you of these quite subtle problems by switching on warnings, as we described in Sect. 1.​3.​3.

If we include the above in a program called Tip.cpp, and compile with the flag to switch on all warnings, then the GNU C++ compiler gives the following warnings:
../images/218075_2_En_2_Chapter/218075_2_En_2_Figad_HTML.gif
We see that, although the offending lines are not doing what was intended, an executable that can be run is still produced. If we compile with the compilation flag -Werror discussed in Sect. 1.​3.​3, then the warnings now become errors, and so no executable program is produced. In this case, we get the following output at compilation time:
../images/218075_2_En_2_Chapter/218075_2_En_2_Figae_HTML.gif

2.6.4 Tip 4: Never Ending while Loops

As discussed briefly in Sect. 2.3, it is essential to ensure that the code can always leave a while loop. The code below was written to find the maximum of an array of four positive numbers called positive_numbers. Why will this code never leave the while loop?
../images/218075_2_En_2_Chapter/218075_2_En_2_Figaf_HTML.gif
The problem with the code above is that the integer count is not incremented inside the while statement. The variable count will therefore always take the value 0, the condition count < 4 will always be satisfied, and the code will never exit the while loop.

2.6.5 Tip 5: Comparing Two Floating Point Numbers

If i and j have been declared as integers, and we want to set another integer variable k to zero if these variables take the same value, then this may easily be written in C++ using the following code.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figag_HTML.gif
Suppose, instead, we wanted to set k to zero if two double precision floating point variables p and q take the same value. It may be thought that a very simple modification of the code above will suffice, where p and q are declared as double precision floating point variables and the guard in line 2 of the listing is modified to test for equality of p and q. This, however, is not the case. Operations between floating point numbers all induce rounding errors. As a consequence, if the true value of a calculation is 5, the number stored may be 5.000000000000186. Testing two double precision floating point variables for equality is unlikely to give the expected answer, as due to rounding errors it is unlikely that two such variables will ever be equal. Instead, we should check that the two numbers differ by less than some very small number,2 as shown below.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figah_HTML.gif

2.7 Exercises

2.1

Below is an example fragment of code that uses several features introduced in this chapter. The variables x, y and z are all double precision floating point variables.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figai_HTML.gif
  1. 1.

    Explain, in words, what the fragment of code does.

     
  2. 2.
    What value would the fragment of code assign to the variable z when the variables x and y take the following values:
    1. (a)

      x = 10.0, and y = -1.0;

       
    2. (b)

      x = 10.0, and y = 20.0; and

       
    3. (c)

      x = 0.0, and y = 20.0.

       
     
  3. 3.

    Modify the code above so that the condition x > y is replaced by x $$\ge $$ y.

     

2.2

Below is some example code. The exercises below all require modification of this code. In all cases use a suitable check to ensure your code is correct.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figaj_HTML.gif
  1. 1.

    Set the variable x to the value 5 if either p is greater than or equal to q, or the variable j is not equal to 10.

     
  2. 2.

    Set the variable x to the value 5 if both y is greater than or equal to q, and the variable j is equal to 20. If this compound condition is not met, set x to take the same value as p.

     
  3. 3.
    Set the variable x according to the following rule.
    $$\begin{aligned} \texttt {x} = \left\{ \begin{array}{lll} {0,} \quad {\texttt {p}&gt; \texttt {q},} \\ {1,} \quad {\texttt {p} \le \texttt {q},\,\mathrm {and}\,\texttt {j} = 10,} \\ {2,} \quad {\mathrm {otherwise}.} \end{array} \right. \end{aligned}$$
     

2.3

In this exercise you are asked to write and test a program which sums a list of numbers which are provided by a user via std::cin (see Sect. 1.​5.​2).

  1. 1.

    Write a program that calculates the sum of a collection of positive integers that are entered by the user from the keyboard. Your program should prompt the user to enter each integer followed by the return key, and to enter “−1” at the end of the list of integers to be added. Note that there is no need to store the list of integers: you can keep track of the sum as the user is entering the values.

     
  2. 2.

    Modify your code so that the code terminates if the sum of integers entered up to that point exceeds 100.

     
  3. 3.

    Modify your code so that, if the user has entered an incorrect integer, they may enter “−2” to reset the sum to zero and begin entering integers again.

     

2.4

This exercise uses the following vectors and matrices:
$$\begin{aligned} \begin{array}{*{20}c} {u = \left( \begin{aligned} 1 \\ 2 \\ 3 \\ \end{aligned} \right) ;} &amp;{} {v = \left( \begin{aligned} 6 \\ 5 \\ 4 \\ \end{aligned} \right) ;} &amp;{} {A = \left( \begin{aligned} \begin{array}{*{20}c} 1 &amp;{} 5 &amp;{} 0 \\ \end{array} \\ \begin{array}{*{20}c} 7 &amp;{} 1 &amp;{} 2 \\ \end{array} \\ \begin{array}{*{20}c} 0 &amp;{} 0 &amp;{} 1 \\ \end{array} \\ \end{aligned} \right) ;} &amp;{} {B = \left( \begin{aligned} \begin{array}{*{20}c} { - 2} &amp;{} 0 &amp;{} 1 \\ \end{array} \\ \,\,\,\,\begin{array}{*{20}c} 1 &amp;{} 0 &amp;{} 0 \\ \end{array} \\ \begin{array}{*{20}c} {\,\,\, 4} &amp;{} 1 &amp;{} 0 \\ \end{array} \\ \end{aligned} \right) .} \\ \end{array} \end{aligned}$$
Furthermore, the vector w satisfies $$w=u-3v$$. These vectors and matrices are stored in arrays using the following program. This program includes code to calculate the vector w.
../images/218075_2_En_2_Chapter/218075_2_En_2_Figak_HTML.gif
We now define vectors x, y, and z, and matrices C and D, such that
$$\begin{aligned}&amp;x = u - v, \\&amp;y = Au, \\&amp;z = Au - v, \\&amp;C = 4A - 3B, \\&amp;D = AB. \\ \end{aligned}$$
Develop the program above to calculate the vectors x, y, and z and the matrices C and D, using loops where possible. Hint: make sure you define arrays of an appropriate size for these variables. Check your answer by printing out the results, and comparing with direct calculation.

2.5

The inverse of a 2$$\times $$2 square matrix is given in Sect. A.​1.​3.

  1. 1.
    Write code to calculate the inverse of the matrix given by
    $$\begin{aligned} A = \left( \begin{array}{*{20}c} 4 &amp;{} {10} \\ 1 &amp;{} 1 \end{array}\right) . \end{aligned}$$
     
  2. 2.

    Check that the inverse calculated is correct by printing out the entries of the inverse, and comparing with direct calculation.

     
  3. 3.

    Modify your code to include an assert statement that checks that the determinant of the matrix is nonzero.

     

2.6

The Newton–Raphson method (see, for example, Kreyszig [2]) is often used to solve nonlinear equations of the form $$f(x) = 0$$. This is an iterative algorithm: given an initial guess x$$_{0}$$, successive iterates satisfy
$$\begin{aligned} x_i = x_{i - 1} - \frac{{f(x_{i - 1} )}}{{f'(x_{i - 1} )}},\quad i = 1,2,3, \ldots . \end{aligned}$$
This algorithm may be terminated when $$|x_{i}-x_{i-1}|&lt; \varepsilon $$ for some user-prescribed $$\varepsilon $$.

In this exercise, we will apply the Newton–Raphson algorithm to the function f(x) = e$$^{\textit{x}}$$+x$$^{3}$$ $$-$$5, with initial guess x$$_{0}$$ = 0.

  1. 1.

    Write down (on paper) the Newton–Raphson iteration for this choice of f(x).

     
  2. 2.

    By using a for loop, and an array for the iterates x$$_{\textit{i}}$$, write a program that implements the Newton–Raphson iteration for i = 1,2,3,..., 100. Print out the value of x$$_{\textit{i}}$$ on each iteration, and confirm that the iteration does converge as i increases. At this stage, do not worry about terminating the iteration when $$\varepsilon $$ is sufficiently small.

     
  3. 3.

    Think of a check that can be performed on the iterates x$$_{\textit{i}}$$, as i becomes larger, that allows you to have confidence that your solution is correct. Implement this check in your program.

     
  4. 4.

    It is not necessary to store the value of x$$_{\textit{i}}$$ on each iteration to implement the Newton–Raphson algorithm. All that is needed is the previous iterate, $$x_{{i}-1}$$, and the current iterate, x$$_{\textit{i}}$$. Modify your code so that the array representing $$x_{i}, \textit{i}=1,2,\ldots , 100$$ is replaced by two scalar variables, x_prev and x_next.

     
  5. 5.

    Modify your code so that, by use of a while statement, the iteration terminates when |x_next-x_prev|$$&lt;\varepsilon $$. Investigate the use of different values of $$\varepsilon $$.