[Courses] [C] Debugging (Re: help me find my C bug? (long))

Mary mary-linuxchix at puzzling.org
Wed Jul 10 12:03:15 EST 2002


Alright, I'm going to work through this and perhaps teach you some
debugging techniques.

On Tue, Jul 09, 2002 at 03:57:18PM -0700, Suzi Anvin wrote:
> Basically, ONLY when there is one penny and no dimes or nickels, it 
> doesn't print that there is one penny left!  and I have no clue why this 
> might be.  Here is the text of the code:

OK, let's see what happens when we put in 1.01, which was an example
that didn't work.

Are you familiar with putting in debugging print statements.

If not, here's the idea:
 
 * You have something that's going wrong.

 * You figure out which path the code should be taking (ie, what lines
   of code should be executing)

 * You think about what value each variable should have at various
   stages.

 * You print out the relevant variables at different stages to check if
   they *do* have that value.

> #include <stdio.h>
> 
> char line[50];
> float amount;			/* the amount of $ as entered by the user */
> int total;			/* amount * 100, converted to an interger */
> int dollars;			/* # of dollars needed to make change */
> int quart;			/* # of quarters needed */
> int dime;			/* # of dimes needed, etc. */
> int nick;
> int penn;
> 
> int main()
> {
>   while (1) {
>     /* inputting dollar amount */
>     printf("Enter the amount of money to make change for: $");
>     fgets(line, sizeof(line), stdin);
>     if (sscanf(line, "%f", &amount) <= 0)
>       break;
>     while (amount <= 0) {
>       printf("Please enter an amount greater than 0: $");
>       fgets(line, sizeof(line), stdin);
>       if (sscanf(line, "%f", &amount) <= 0)
>          break;
>     }
>     total = amount * 100;

OK, let's insert a line here to check that we're getting the right
total. We expect the total to be 101.

When I add a line saying:
        fprintf (stderr, "Check #1: is total correct?\n\ttotal=%d\n\n", total);

        /* you could use a standard printf here too, I'm choosing to
         * write to standard error rather than standard out like printf
         * would */

I get:
        Check #1: is total correct?
                total=100

so that's WRONG. Now we can see the flow on effect with more print
statements, for illustrative purposes.

>     /* determining how many of each coin is needed */
>     dollars = total / 100;
>     total %= 100;
>     quart = total / 25;
>     total %= 25;
>     dime = total / 10;
>     total %= 10;
>     nick = total / 5;
>     penn = total % 5;

OK, let's see what all these end up as:

        fprintf (stderr, "Check #2.1: is dollars correct?\n\tdollars=%d\n\n", dollars);
        fprintf (stderr, "Check #2.2: is total correct?\n\ttotal=%d\n\n", total);
        fprintf (stderr, "Check #2.3: is quart correct?\n\tquart=%d\n\n", quart);
        fprintf (stderr, "Check #2.4: is total correct?\n\ttotal=%d\n\n", total);
        fprintf (stderr, "Check #2.5: is dime correct?\n\tdime=%d\n\n", dime);
        fprintf (stderr, "Check #2.6: is total correct?\n\ttotal=%d\n\n", total);
        fprintf (stderr, "Check #2.7: is nick correct?\n\tnick=%d\n\n", nick);
        fprintf (stderr, "Check #2.8: is penn correct?\n\tpenn=%d\n\n", penn);

And this is printed:

Check #2.1: is dollars correct?
        dollars=1

Check #2.2: is total correct?
        total=0

Check #2.3: is quart correct?
        quart=0

Check #2.4: is total correct?
        total=0

Check #2.5: is dime correct?
        dime=0

Check #2.6: is total correct?
        total=0

Check #2.7: is nick correct?
        nick=0

Check #2.8: is penn correct?
        penn=0

Now, given that total = 100 at the beginning, then it is correct that
penn ends up as 0.

Now we need to think about why total is ending up as 100, instead of
101.

This is the code that should get us a value of 101:

>     while (amount <= 0) {
>       printf("Please enter an amount greater than 0: $");
>       fgets(line, sizeof(line), stdin);
>       if (sscanf(line, "%f", &amount) <= 0)
>          break;
>     }
>     total = amount * 100;

Since total is just amount times 100, then perhaps amount is wrong.

Insert a Check #0
>     while (amount <= 0) {
>       printf("Please enter an amount greater than 0: $");
>       fgets(line, sizeof(line), stdin);
>       if (sscanf(line, "%f", &amount) <= 0)
>          break;
>     }

      fprintf (stderr, "Check #0: is amount correct?\n\tamount=%f\n\n", amount);
      
>     total = amount * 100;

And we get:

Check #0: is amount correct?
        amount=1.010000

which is right. So how is it being multiplied and not ending up as 101?!

There are a few sources of error when converting a floating point to an
int:

At some point, amount, a float, needs to be converted (you will
normally hear this called a "cast to an integer") to an integer.  C does
this by simply dropping everything after the decimal point.

However, we know that 1.10 is behaving correctly, so it would seem
that the conversion is occuring after the multiplication.

Let's see what the multiplication gives us:

        fprintf (stderr, "Check #0.1: is amount * 100 correct?\n\tamount * 100=%f\n\n", amount * 100);

Check #0.1: is amount * 100 correct?
        amount * 100=100.999999

OK. Now we can see that when this is converted to an int, the .999999
gets dropped, and total ends up as 100!

But why is 1.01 * 100 equal to 100.999999? You've uncovered a quite
subtle set of errors that occur with floating point numbers.

Your next step is reading about floating point errors:

A general tute:
http://www.ug.cs.usyd.edu.au/~cs2/soft2004/s1_2002/tutes/week10/

And an answer to this specific bug:
http://www.eskimo.com/~scs/C-faq/q14.1.html

-Mary.



More information about the Courses mailing list