Hunter Liu's Website

3. Week 1 Thursday: Integer Operations

≪ 2. Week 1 Tuesday: Errors | Table of Contents | 4. Week 2 Tuesday: Trouble with Doubles ≫

You are likely familiar with the arithmetic operations of +-*/, representing addition, subtraction, multplication, and division, respectively. One can add, subtract, multiply, and divide integers in C++, alongside a handful of other fun new operations you may or may not have seen before.

Addition, subtraction, and multiplication work exactly how one expects them to, but integer division is somewhat different than usual. Consider the following snippet of code:

1cout << 10 / 5 << endl; 
2cout << 10 / 7 << endl;
3cout << 5 / 7 << endl; 

Following conventional wisdom and common core elementary math standards, one would expect the output to be something like 2, 1.428571..., and 0.71428571... on three separate lines. However, the actual output is 2, 1, and 0.

Integer Division and the Modulo Operator

Integer division in C++ will always produce an integer and not a decimal. If a division operation would mathematically be a decimal number, C++ will chop off everything past the decimal point and call it a day. This applies to negative numbers as well:

1cout << -9 / 6 << endl; // expected: -1.5, actual: -1 
2cout << 9 / -6 << endl; // same as above ^^^ 
3cout << -21 / -4 << endl; // expected: 5.25, actual: 5 
4// etc... 

Although frustrating to learn about, this particular behaviour with integer division plays a central role in many algorithms, particularly those that need to chop up lists of data into a set number of chunks and in certain computations. An example is division by 10 — if i is a C++ integer and one writes the code i = i / 10, what does this do to i? When might this be useful?

Integer division was born with a very talented brother named the modulo operator, %. This is often introduced as the “remainder” operator, and although this is a perfect characterisation of what % does for positive numbers, it is very misleading and produces confusion for expressions like 21 % -4. What’s the remainder to 21 / -4?

Instead, think about how division and multiplication are supposed to cancel each other out. Mathematically, if \(a\) and \(b\) are two numbers and \(b\) is nonzero, we expect \[(a \div b)\times b = a,\] no matter what. However, this is not the case in C++ — work out what (21 / 4) * 4 is using integer division. The modulo operator’s purpose is to capture this discrepancy! Specifically, if a and b are two integers (negative or not), the quantity a % b is defined as the number satisfying the equation a = (a / b) * b + a % b. When a and b are both positive, this is exactly the same thing as a remainder; however, when either a or b is negative (or both), this alternative form continues to be unambiguous.

Example 1.

Write a program that expresses 1999473 seconds as days, hours, minutes, and seconds.

We’ll do this in steps: we’ll first write 1999473 seconds in minutes and seconds. Since there are 60 seconds to a minute, and since \(1999473\div 60 = 33324.55\), there should be 33324 minutes and some leftover seconds. This 33324 is the result of doing integer division! We divided the number of seconds by \(60\), then chopped off the decimal. We can then use the modulo operator to extract the number of seconds, which should be the remainder of this division.

We can then turn 33324 minutes into hours and minutes, then however many hours into days and hours! In pseudocode:

  1. Define an integer variable seconds and set it to 1999473.
  2. Set minutes = seconds / 60 and replace seconds with seconds % 60.
  3. Set hours = minutes / 60 and replace minutes with minutes % 60.
  4. Set days = hours / 24 and replace hours with hours % 24.
  5. Print out the results to the screen.

Here’s the whole thing converted to C++ code:

 1#include <iostream> 
 2
 3using namespace std; 
 4
 5int main() {
 6    // step 1
 7    int seconds = 1999473; 
 8
 9    // step 2 
10    int minutes = seconds / 60; 
11    seconds = seconds % 60; 
12
13    // step 3 
14    int hours = minutes / 60; 
15    minutes = minutes % 60; 
16
17    // step 4 
18    int days = hours / 24; 
19    hours = hours % 24; 
20
21    // step 5 
22    cout << "There are " << days << " days, "
23         << hours << " hours, " 
24         << minutes << " minutes, and "
25         << seconds << " seconds in 1999473 seconds." << endl; 
26
27    return 0; 
28} 

Some things to note about this:

Problem 2.

Write a program that expresses 302469 inches as miles, feet, and inches.

Increments and Decrements

The operation of adding or subtracting one from a number is a very common operation in C++. It’s used when counting number of occurrences of an event, keeping track of how many times a loop was repeated, and many others. If n is a numerical variable, the increments n++ and ++n add one to n, and the decrements n-- and --n subtract one from n.

Remark 3. Non-numeric variables can be incremented or decremented too.

The increment and decrement operators can be manually extended to non-numeric variables by programmers in a process called “operator overloading”. This is beyond the scope of this class, but the upshot is that some variables that are not numbers can nevertheless be incremented or decremented. We will see examples of this when we talk about pointers in the distant future.

We’ll focus on the increment operators; everything we say below will have analogous statements for the corresponding decrement operators.

One can think of both n++ and ++n as shortcuts for writing the statement n += 1. When n++ and ++n stand alone, the behaviour is identical:

1int n = 5;
2n++;
3cout << n << endl; // prints 6
4++n;
5cout << n << endl; // prints 7

However, there is one important difference: you can use n++ and ++n within a larger piece of code. This is where all the power of these operators comes in!

1int n = 5;
2cout << ++n << endl; // prints 6
3cout << n++ << endl; // prints ...6??

The above code highlights a subtlety of the two operators. ++n says to first increase n by one, then substitute its value into the code. Likewise, the n++ operator says to first substitute the value of n into the code, then increment its value.

Be very careful about using multiple increment/decrement operators in the same expression. One might be tempted to think that (n++)++ will increment n twice, but this does not even compile. The code (++n)++ does increment n twice (it increments n once, substitutes that value in, then increments again). However, the code ++(n++) does not compile.

Similarly, statements such as n++ + ++n, n + ++n, n * ++n, etc. produce undefined behaviour according to the C++ standard. That is, the code will compile, but you may get different results depending on your compiler and your computer.

The issue is one of the order of operations — C++ needs to substitute the values of n and ++n into the expression, but the order in which this substitution occurss both affects the resultant value and changes depending on the compiler. There are reasons for why these operators behave like this, but this is well beyond the scope of PIC 10A.

That being said, if one has two variables n and m, expressions such as ++n * ++m are perfectly okay. It’s when a single variable is being incremented or decremented and gets used more than once.

Example 4.

Predict the output of the following code:

 1#include <iostream>
 2
 3using namespace std;
 4
 5int main() {
 6    int n = 15;
 7    int m = 17;
 8
 9    cout << n++ + ++m << endl;
10    cout << ++n + m-- << endl;
11    cout << --m * n-- << endl;
12    cout << m << endl;
13    cout << n << endl;
14
15    return 0;
16}
Solution

My preferred approach to these kinds of problems is to keep track of the values of n and m after each line of code. In the table below, the columns for n and m represent their values after the line has been executed.

Line Number Output n m
8 N/A 15 17
9 (15 + 18 = ) 33 16 18
10 (17 + 18 = ) 35 17 17
11 (16 * 17 = ) 272 16 16
12 16 16 16
13 16 16 16

Thus, the output consists of the five numbers 33, 35, 272, 16, and 16, each on their own line.