Let’s use the for
statement to compute compound interest. Consider the following problem:
A person invests $1,000 in a savings account yielding 5% interest. Assuming that all the interest is left on deposit, calculate and print the amount of money in the account at the end of each year for 10 years. Use the following formula to determine the amounts:
where
p is the original amount invested (i.e., the principal)
r is the annual interest rate (e.g., use 0.05 for 5%)
n is the number of years
a is the amount on deposit at the end of the nth year.
The solution to this problem (Fig. 5.6) involves a loop that performs the indicated calculation for each of the 10 years the money remains on deposit. For this solution, we use double
values for the monetary calculations, then we discuss the problems with using floating-point types to represent monetary amounts. In the next section, we’ll develop a new DollarAmount
class that uses very large integers to precisely represent monetary amounts. As you’ll see, the class performs monetary calculations using only integer arithmetic.
Lines 12–13 in main
declare double
variables principal
and rate
, and initialize principal
to 1000.00
and rate
to 0.05
. C++ treats floating-point literals like 1000.00
and 0.05
as type double
. Similarly, C++ treats whole-number literals like 7
and -22
as type int
. Lines 15–16 display the initial principal and the interest rate.
The output statement in line 10 before the for
loop and the output statement in line 27 in the for
loop combine to print the values of the variables year
and amount
with the formatting specified by the parameterized stream manipulators setprecision
and setw
and the nonparameterized stream manipulator fixed
. The stream manipulator setw(4)
specifies that the next value output should appear in a field width of 4—i.e., cout <<
prints the value with at least four character positions. If the value to be output is less than four character positions wide, the value is right justified in the field by default. If the value to be output is more than four character positions wide, the field width is extended with additional character positions to the right to accommodate the entire value. To indicate that values should be output left justified, simply output nonparameterized stream manipulator left
(found in header <iostream>
). Right justification can be restored by outputting nonparameterized stream manipulator right
.
The other formatting in the output statements indicates that variable amount
is printed as a fixed-point value with a decimal point (specified in line 10 with the stream manipulator fixed
) right justified in a field of 20 character positions (specified in line 27 with setw(20)
) and two digits of precision to the right of the decimal point (specified in line 10 with manipulator setprecision(2)
). We applied the stream manipulators fixed
and setprecision
to the output stream cout
before the for
loop because these format settings remain in effect until they’re changed—such settings are called sticky settings and they do not need to be applied during each iteration of the loop. However, the field width specified with setw
applies only to the next value output. We discuss C++’s powerful input/ output formatting capabilities in Chapter 13, Stream Input/Output: A Deeper Look.
pow
The for
statement (lines 22–28) executes its body 10 times, varying the unsigned int
control variable year
from 1 to 10 in increments of 1. This loop terminates when year
becomes 11. Variable year
represents n in the problem statement.
C++ does not include an exponentiation operator, so we use the standard library function pow
(line 24) from the header <cmath>
. The call pow(x, y)
calculates the value of x raised to the yth power. The function receives two double
arguments and returns a double
value. Line 24 performs the calculation , where a is amount
, p is principal
, r is rate
and n is year
.
The body of the for
statement contains the calculation 1.0 + rate
as pow
’s first argument. This calculation produces the same result each time through the loop, so repeating it in every iteration of the loop is wasteful.
In loops, avoid calculations for which the result never changes—such calculations should typically be placed before the loop. Many of today’s sophisticated optimizing compilers will place such calculations before loops in the compiled code.
Variables of type float
represent single-precision floating-point numbers and have approximately seven significant digits on most of today’s systems. Variables of type double represent
double-precision floating-point numbers. These require twice as much memory as float
variables and provide approximately 15 significant digits on most of today’s systems—approximately double the precision of float
variables. Most programmers represent floating-point numbers with type double
. In fact, C++ treats all floating-point numbers you type in a program’s source code (such as 7.33 and 0.0975) as double
values by default. Such values in the source code are known as floating-point literals. See Appendix C for the fundamental types’ value ranges.
In conventional arithmetic, floating-point numbers often arise as a result of division—when we divide 10 by 3, the result is 3.3333333…, with the sequence of 3s repeating infinitely. The computer allocates only a fixed amount of space to hold such a value, so clearly the stored floating-point value can be only an approximation. As you can see, double
suffers from what we call representational error.
Using floating-point numbers in a manner that assumes they’re represented exactly (e.g., using them in comparisons for equality) can lead to incorrect results. Floating-point numbers are represented only approximately.
Floating-point numbers have numerous applications, especially for measured values. For example, when we speak of a “normal” body temperature of 98.6 degrees Fahrenheit, we do not need to be precise to a large number of digits. When we read the temperature on a thermometer as 98.6, it may actually be 98.5999473210643. Calling this number simply 98.6 is fine for most applications involving body temperatures. Due to the imprecise nature of floating-point numbers, type double
is preferred over type float
, because double
variables can represent floating-point numbers more precisely. For this reason, we use type double
throughout the book.
We declared variables amount
, principal
and rate
to be of type double
in this example. We’re dealing with fractional parts of dollars and thus need a type that allows decimal points in its values. Unfortunately, floating-point numbers can cause trouble. Here’s a simple explanation of what can go wrong when using double
(or float
) to represent dollar amounts (assuming that dollar amounts are displayed with two digits to the right of the decimal point): Two calculated double
dollar amounts stored in the machine could be 14.234 (which would normally be rounded to 14.23 for display purposes) and 18.673 (which would normally be rounded to 18.67 for display purposes). When these amounts are added, they produce the internal sum 32.907, which would normally be rounded to 32.91 for display purposes. Thus, your output could appear as
but a person adding the individual numbers as displayed would expect the sum to be 32.90. You’ve been warned!
Do not use variables of type double
(or float
) to perform precise monetary calculations. The imprecision of floating-point numbers can lead to errors.
Even simple dollar amounts, such as those you might see on a grocery or restaurant bill, can have representational errors when they’re stored as double
s. To see this, we created a simple program with the declaration
double d = 123.02;
then displayed variable d
’s value with many digits of precision to the right of the decimal point—we ask you to do this in Exercise 5.36. The resulting output showed 123.02 as 123.0199999…, which is another example of a representational error. Though some dollar amounts can be represented precisely as double
, many cannot. In the next section, we’ll build a class that represents and processes dollar amounts using only integer arithmetic. This eliminates the representational errors associated with double
values, because integers are represented exactly.