Skip to main content

Section 8.7 Tables

One of the things loops are good for is generating tabular data. For example, before computers were readily available, people had to calculate logarithms, sines and cosines, and other common mathematical functions by hand. To make that easier, there were books containing long tables where you could find the values of various functions. Creating these tables was slow and boring, and the result tended to be full of errors.
When computers appeared on the scene, one of the initial reactions was, β€œThis is great! We can use the computers to generate the tables, so there will be no errors.” That turned out to be true (mostly), but shortsighted. Soon thereafter computers and calculators were so pervasive that the tables became obsolete.
Well, almost. It turns out that for some operations, computers use tables of values to get an approximate answer, and then perform computations to improve the approximation. In some cases, there have been errors in the underlying tables, most famously in the table the original Intel Pentium used to perform floating-point division.
Although a β€œlog table” is not as useful as it once was, it still makes a good example of iteration.
Listing 8.7.1. This active code outputs a sequence of values in the left column and their base 2 logarithms in the right column.
The sequence \t represents a tab character. A tab character causes the cursor to shift to the right until it reaches one of the tab stops, which are normally every eight characters. This helps the second column stay lined up even when the length of the number in the first column goes from 1 to 2 digits. If we used the string " " to space out the columns, the second column would jump over when the first column changed length.
When making a table, or doing any work with a loop, it is important to think about what variable really is the loop control variable. Say you are asked to print a table of investment income like the one below. The investment starts at $10,000 and grows by 20% until it reaches at least $100,000:
Year	Investment Amount
------------------------
1	$10000
2	$12000
3	$14400
4	$17280
5	$20736
6	$24883.2
7	$29859.8
8	$35831.8
9	$42998.2
10	$51597.8
11	$61917.4
12	$74300.8
13	$89161
14	$106993
Stop and think about what is really controlling that loop? Does the problem specify how many months the process takes? No - it says that we want to stop when we hit $100,000+ not after X months. (Though you could do math to figure out the number of months.) So despite counting from 1 to 14, this isn’t really a counting loop. It is a sentinel value loop that is looking for the balance to become $100,000+. Here is what the program might look like:
Listing 8.7.2.
Key things to note:
  • balance is the loop control variable. It is serving as an accumulator to store the interest earned each year.
  • years is a counter variable we increase by one each time through the loop, but the loop logic does not depend on its value.
  • There are multiple updates that need to happen each year. We need to increment years, calculate the interest based on the current balance, and then update the balance.
  • The loop does not print the last row of the table. After the updates are done, if we have hit the TARGET, the loop stops without printing the final value. So we do one last print statement after the loop to handle that row.
  • Instead of magic numbers for START_VALUE, GROWTH_RATE,and TARGET, we have constants. When values in a program should be reconfigurable, but do not come from input, it is a good idea to make them constants and declare them together in one place.

Insight 8.7.1.

It is often necessary (or at least easier) to handle the first or last item in a sequence before or after the loop that handles the other items.
Doing this is better than putting an if inside the loop to do something special only in the first or last iteration. We could move the final output inside the loop above by doing:
Listing 8.7.3.
    ...
    balance += interest;
    if (balance >= TARGET) {
        cout << years << "\t$" << balance << endl;
    }
}
That solution needless complicates every iteration of the loop to guard against the special case that we are actually now finished.

Checkpoint 8.7.1.

Let’s write the code that prints out the powers of two up to 16.

Checkpoint 8.7.2.

Checkpoint 8.7.3.

How can we modify the code below to print out a table of the first five odd numbers and their perfect cubes?
int main() {
  int x = 1;
  while (x < 11) {
    cout << x << "\t" << pow(x, 2) << endl;
    x = x + 1;
  }
}
  • Change pow(x,2) to pow(3,x) and change x = x + 1 to x = x + 2.
  • Check the order of the pow function!
  • Change pow(x,2) to pow(x,3).
  • This will print out the first ten perfect cubes.
  • Change pow(x,2) to pow(x,3) and change x = x + 1 to x = x + 2.
  • Changing both the pow function and the increment in this way gives us the right answer.
  • Change x < 11 to x < 6 and change pow(x,2) to pow(x,3).
  • This will print out the first five perfect cubes, but not the first five odd perfect cubes.
You have attempted of activities on this page.