Hunter Liu's Website

7. Week 3 Thursday: If Statements

≪ 6. Week 3 Tuesday: Handling Input with Strings and cin | Table of Contents | 8. Week 4 Tuesday: Practise with Loops! ≫

If Statements and Conditionals

If statements are our first example of control flow, and their syntax is as follows:

1if( [statement] ) {
2    // code... 
3} 

The statement, called a conditional, must be a boolean expression. The code within the curly braces only runs if the conditional is true. You can also chain if statements with an else if or an else block:

 1int x = 73;
 2if(x >= 90) {
 3    cout << "You got an A" << endl;
 4} else if(x >= 80) {
 5    cout << "You got a B" << endl;
 6} else if(x >= 70) {
 7    cout << "You got a C" << endl;
 8} else if(x >= 60) {
 9    cout << "You got a D" << endl;
10} else {
11    cout << "See me after class." << endl;
12}

This snippet of code will output You got a C. It will check the condition x >= 90, which is false. Then, it checks x >= 80, which is also false. It finally checks x >= 70, which is true, and so it runs the following code block. It then skips the rest of the else if/else blocks!

There are several things to remark:

1int x = 50;
2if(x < 0)
3    cout << "I love ";
4    cout << "pie" << endl;

Let’s look a bit closer at these boolean conditions. Most often, you’ll be performing a comparison, and we’ve already seen these before: <, >, <=, and >= all do exactly what you think they do. They can only be used to compare two numeric quantities — integers, doubles, characters, etc. You can also compare if two quantities are equal using two equal signs:

 1int x = 37; 
 2cout << "Guess my secret number: " << endl; 
 3int input; cin >> input; 
 4
 5if(input == x) { 
 6    cout << "You guessed it!" << endl; 
 7} else {
 8    cout << "Haha I'm smarter than you " 
 9         << "*spits in your face*" << endl; 
10} 

Common Mistake 1.

When comparing two values, you have to use two equal signs ==. If you use one equal sign =, the code will still compile, but it won’t perform the comparison and may produce unexpected behaviour.

Common Mistake 2.

To check if an integer i is between 1 and 100, it may be tempting to write

1int i; cin >> i; 
2if(1 <= i <= 100) {
3    cout << "Your number is between 1 and 100!" << endl; 
4} 

Although this code compiles, it doesn’t always behave the way we expect. Instead, we need to write 1 <= i && i <= 100!

To check if two quantities are not equal, use !=. As a demonstration, consider the following code:

1cout << boolalpha; 
2cout << (20.15 * 100 != 2015) << endl; 

This will print true if 20.15 * 100 is not 2015, false otherwise. Against all common knowledge, this prints false. This is because of the limited precision of doubles in C++! You should in general stay away from using == with doubles.

Sometimes, however, we may have more than one condition that we want to be true, or several conditions of which we want at least one to be true. If p and q are two boolean expressions (i.e., true or false, such as the comparisons above), you can combine them in the following two ways:

Of course, one can string together any number of conditions using a mix of && and ||; however, you should be careful with the order in which they are combined together. This StackOverflow answer provides a great explanation; in short, && is evaluated first in absence of parentheses.

In addition to this “order of operations” business, C++ engages with something called lazy evaluation. Consider the following two somewhat contrived programs:

Program 1
 1#include <iostream> 
 2#include <string> 
 3
 4using namespace std; 
 5
 6int main() {
 7    cout << "Please enter a word: " << endl; 
 8    string word; cin >> word; 
 9
10    if(word.length() >= 5 && word.at(4) == 'a') {
11        cout << "You entered a very special word." << endl; 
12    } else {
13        cout << "Your word is kind of lame and stinky." << endl; 
14    } 
15
16    return 0; 
17} 
Program 2
 1#include <iostream> 
 2#include <string> 
 3
 4using namespace std; 
 5
 6int main() {
 7    cout << "Please enter a word: " << endl; 
 8    string word; cin >> word; 
 9
10    if(word.at(4) == 'a' && word.length() >= 5) { 
11        cout << "You entered a very special word." << endl; 
12    } else {
13        cout << "Your word is kind of lame and stinky." << endl; 
14    } 
15
16    return 0; 
17} 

When providing the input hi, the first program runs perfectly well while the second program crashes. This is because C++ evaluates the two conditions in order. In the first program, it checks if the word.length() >= 5 first. Seeing that it’s false, it has no reason to evaluate the second condition — whether it’s true or not has no impact on the value of the conditional — hence it skips checking word.at(4) and doesn’t crash the program. However, in the second program, it does things in the reverse order and tries to access a nonexistent index.

Problem 3. Evenly Spaced

Write a program that asks the user for three integers, then prints “Evenly spaced” if the three numbers are evenly spaced, and “Unevenly spaced” otherwise. For instance, the numbers 2, 4, and 6 are evenly spaced, regardless of which order they are input, but 7, 10, and 14 are not.

Let’s analyse this particular problem together. A naïve but entirely plausible solution in pseudocode may look as follows:

  1. Let the user enter three integers, \(a\), \(b\), and \(c\).
  2. If \(b-a=c-b\), the numbers are evenly spaced.
  3. Otherwise, the numbers are not evenly spaced.

This works perfectly well when the numbers are in increasing or decreasing order — we’re checking if the differences between consecutive numbers are the same.

However, this algorithm falls apart entirely when the three numbers are out of order: the user inputting 4 2 6 would cause a = 4, b = 2, c = 6 to be read in, and b - a = -2 while c - b = 4.

One immediate way to remedy this is to first put the numbers in order, then perform the above algorithm. In pseudocode,

  1. Let the user enter three integers, \(a\), \(b\), and \(c\).
  2. By swapping the contents of \(a\), \(b\), and/or \(c\) as necessary, put the three numbers in ascending or descending order.
  3. If \(b-a=c-b\), the numbers are evenly spaced.
  4. Otherwise, they are not evenly spaced.

Try to implement these steps algorithmically before peeking at the solution!

Solution

This is not the only way to solve this problem; there are may different ways to place numbers in increasing or decreasing order. We will handle this by breaking up all the possible scenarios into distinct cases.

Suppose the user has entered the integers \(a, b, c\); our job is to place them in either ascending or descending order.

  1. If \(a \leq b \leq c\), we have nothing to do.
  2. If \(c \leq b \leq a\), we also have nothing to do.
  3. If \(a \leq c < b\), we should swap \(b\) and \(c\).
  4. If \(c \leq a < b\), we should swap \(a\) and \(b\).
  5. If \(b < a \leq c\), we should swap \(a\) and \(b\).
  6. If \(b < c \leq a\), we should swap \(b\) and \(c\).

The above analysis represents every possible ordering of the three inputs; we are describing how to handle each case.

We could write four if/else-if branches for scenarios 3-6, but this would result in some redundancies: the actions we need to perform for cases 4 and 5 are the same. Thus, we may write this in another way, grouping by the actions we are performing rather than the scenario:

  • We should swap \(a\) and \(b\) when…
    • \(b < a \leq c\) (case 5)
    • \(b > a \geq c\) (case 4)
  • We should swap \(b\) and \(c\) when…
    • \(b < c \leq a\) (case 6)
    • \(b > c \geq a\) (case 3)

Converting this into actual code yields:

 1#include <iostream> 
 2
 3using namespace std; 
 4
 5int main() {
 6    // 1. read user input into 3 integers, a, b, and c 
 7    int a, b, c; 
 8    cout << "Please enter 3 integers: "; 
 9    cin >> a >> b >> c; 
10
11    // 2. rearrange the three integers so they're "in order". 
12    // cases 4 and 5, swapping a and b
13    if((b < a && a >= c) || (b > a && a >= c)) { 
14        int temp = b; 
15        b = a; 
16        a = temp; 
17    } 
18
19    // cases 3 and 6, swapping b and c
20    else if((b < c && c <= a) || (b > c && c >= a)) {
21        int temp = b; 
22        b = c; 
23        c = temp; 
24    } 
25
26    // 3. if b - a and c - b are equal, we are evenly spaced 
27    if( b - a == c - b ) {
28        cout << "Evenly spaced" << endl; 
29    } 
30
31    // 4. otherwise, they aren't evenly spaced. 
32    else {
33        cout << "Unevenly spaced" << endl; 
34    } 
35
36    return 0; 
37} 
Challenge
There is a solution to this problem that does not involve rearranging the user’s three inputs. Can you think of a way to do this?