Cautions With for Loops
The Program
/*****************************************
Program: beers.c
Author: An unknown UMBC Student
Date: Every Semester
Section: Can never remember
Email: unknown@umbc.edu
How many beers are left after 5 rounds?
*****************************************/
#include
int main()
{
/* Declare variable */
float beers ;
/* You'd think this loop will count down from
** 1.0 to 0.0 by the step -0.2, singing each verse
** as it goes, but does it ? */
for (beers = 1.0; beers != 0.0; beers -= 0.2)
{
printf("%f %s\n%f %s\n",
beers, "bottles of beer on the wall",
beers, "bottles of beer");
printf("Take a fifth down, pass it around\n");
printf("%e bottles of beer on the wall\n\n", beers - 0.2 );
}
return 0;
}
The Sample Run
1.000000 bottles of beer on the wall
1.000000 bottles of beer
Take a fifth down, pass it around
8.000000e-01 bottles of beer on the wall
0.800000 bottles of beer on the wall
0.800000 bottles of beer
Take a fifth down, pass it around
6.000000e-01 bottles of beer on the wall
0.600000 bottles of beer on the wall
0.600000 bottles of beer
Take a fifth down, pass it around
4.000000e-01 bottles of beer on the wall
0.400000 bottles of beer on the wall
0.400000 bottles of beer
Take a fifth down, pass it around
2.000000e-01 bottles of beer on the wall
0.200000 bottles of beer on the wall
0.200000 bottles of beer
Take a fifth down, pass it around
3.278255e-08 bottles of beer on the wall
0.000000 bottles of beer on the wall
0.000000 bottles of beer
Take a fifth down, pass it around
-2.000000e-01 bottles of beer on the wall
-0.200000 bottles of beer on the wall
-0.200000 bottles of beer
Take a fifth down, pass it around
-4.000000e-01 bottles of beer on the wall
-0.400000 bottles of beer on the wall
-0.400000 bottles of beer
Take a fifth down, pass it around
-6.000000e-01 bottles of beer on the wall
-0.600000 bottles of beer on the wall
-0.600000 bottles of beer
Take a fifth down, pass it around
-8.000000e-01 bottles of beer on the wall
-0.800000 bottles of beer on the wall
-0.800000 bottles of beer
Take a fifth down, pass it around
-1.000000e+00 bottles of beer on the wall
-1.000000 bottles of beer on the wall
-1.000000 bottles of beer
Take a fifth down, pass it around
-1.200000e+00 bottles of beer on the wall
The Lesson
- Don't test floating point numbers for equality.
- The culprit: 1/5 is not a terminating fractional number in
base 2 (similar to 1/3 in base 10).
Fractions in base 2
In base ten (decimal), we use 0.1 to represent 1/10, 0.01 to represent
1/100, 0.001 to represent 1/1000, and so on. In base two (binary), we
use 0.1 to represent 1/2, 0.01 to represent 1/4, and 0.001 to
represent 1/8, etc.
We can determine the binary representation of a fraction using the
following strategy. Suppose the number is stored in the (float or
double) variable fraction. Then we can print out each digit
of the number's binary representation as follows:
while (fraction > 0)
{
fraction = fraction * 2 ;
if (fraction >= 1.0)
{
digit = 1 ;
fraction = fraction - 1.0 ;
}
else
{
digit = 0 ;
}
printf("%d\n", digit) ;
}
Using this strategy, we can figure out that the binary representation
of 5/8 = 0.625 (base 10) is 0.101 (base 2).
The value of the variables fraction and digit
during each iteration of the loop are (in base 10):
iteration fraction digit
1 0.625
1.25 1
0.25
2 0.25
0.5 0
3 0.5
1.0 1
0.0
We can check our answer by noting that 1/2 + 1/8 = 5/8, which is what
0.101 (base 2) means.
If we try the same for 1/5, we will notice that the loop never ends.
So, 1/5 cannot be represented by a finite number of digits in base 2.
This is analogous to the fact that 1/3 is cannot be represented using
a finite number of digits in base 10.
iteration fraction digit
1 0.2
0.4 0
2 0.4
0.8 0
3 0.8
1.6 1
0.6
4 0.6
1.2 1
0.2
5 0.2
0.4 0
6 0.4
0.8 0
7 0.8
1.6 1
0.6
8 0.6
1.2 1
9 0.2
0.4 0
10 0.4
0.8 0
11 0.8
1.6 1
0.6
12 0.6
1.2 1
...
So, 1/5 is 0.0011001100110011... (base 2) repeating.