1

I am working on C program with float values, below is my code.

#include<stdio.h>
#include<stdlib.h>
#include<float.h>   

int main()
{
    int counter = 0;
    float quarter = 0.25;
    float dime = 0.10;
    float nickel = 0.05;
    float penny = 0.01;

    float change = 0.00;

    printf("hi, how much do i owe u?\t");
    scanf("%f", &change);

    while(change > 0.0)
    {
        if(change >= quarter)
        {
            change -= quarter;
            printf("quarter %.2f\n", quarter);
        }
        else if(change >= dime)
        {
            change -= dime;
            printf("dime %.2f\n", dime);
        }
        else if(change >= nickel)
        {
            change -= nickel;
            printf("nickel %.2f\n", nickel);
        }
        else if(change >= penny)
        {
            change -= penny;
            printf("penny %.2f\n", penny);
        }

        counter++;  
    }

    printf("your count is %i\n", counter);

    return 0;
}

Output is:

hi, how much do i owe u?    .45
quarter 0.25
dime 0.10
nickel 0.05
penny 0.01
penny 0.01
penny 0.01
penny 0.01
`^C`

I have to press ctrl c to terminate loop

Last printf("your count is %i\n", counter); does not execute at all - to count # of coins used

If i replace float type with int it works OK.

Please help with this problem

mc110
  • 2,825
  • 5
  • 20
  • 21
kasper_341
  • 85
  • 1
  • 9
  • 1
    Comparing floats is usually pretty error-prone. See here more about it: http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison – dragosht Jun 20 '14 at 13:59
  • 4
    @dragosht The problem here is not comparing floats, the problem is using floats to represent 0.10, 0.05 and 0.01. You could write a financial program that involves no comparison whatsoever and it would still compute differently from what you would expect if you thought that these numbers were represented exactly. – Pascal Cuoq Jun 20 '14 at 14:30
  • 1
    Don't use floating point for money. – R.. GitHub STOP HELPING ICE Jun 20 '14 at 15:41
  • "I have to press ctrl c to terminate loop", looks like the loop would have continued 1 more time with a penny and then may have looped infinitely as these was not `else` after the `else if(change >= penny)` to catch `float` residual values. Using `float` for money is challenging to do correctly. Common learner approach is to use `int` values of cents. – chux - Reinstate Monica Jun 21 '14 at 05:45
  • You may refer to this wiki page which explains well why do not use float as counter:https://wiki.sei.cmu.edu/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters – Yong Yang Mar 03 '22 at 09:18

4 Answers4

3

Adding this at the beginning of your loop:

if (change < penny) {
    printf("remaining: %.10f\n", change);
    break;
}

Will issue this output:

hi, how much do i owe u?        .45
quarter 0.25
dime 0.10
nickel 0.05
penny 0.01
penny 0.01
penny 0.01
penny 0.01
remaining: 0.0099999849
your count is 7

This is caused by the internal float numbers representation. There's an intrinsic error - most float numbers do not have an exact representation - they're just really good approximates.

dragosht
  • 3,237
  • 2
  • 23
  • 32
  • 1
    Actually float numbers have an exact representation, in binary and in decimal. What you mean is that real numbers, even those that have an exact representation in decimal, do not have an exact representation as float numbers. – Pascal Cuoq Jun 20 '14 at 14:27
  • 1
    And this is why you never use floating point types for monetary transactions. – John Bode Jun 20 '14 at 14:27
  • @dragosht - i tried your suggestions and it works, – kasper_341 Jun 21 '14 at 15:17
2

Do yourself a huge favor. Take the inputted value, multiply it by 100, and do all of your calculations in whole cents rather than in dollars. Only your printed outputs should be converted to dollars-and-cents.

Computers are much more accurate with integers than they are with floats.

hymie
  • 1,982
  • 1
  • 13
  • 18
0

Print out change at the end of your loop, and see what it becomes at the very end. My guess is that it will be some very small positive number due to floating point errors. You can fix this by simply changing your while statement to be change > 1.e-3 or some other smaller number.

wolfPack88
  • 4,163
  • 4
  • 32
  • 47
  • Printing `change` at the end of while loop through's it into infinite cycle, I've no idea why ?? will investigate later. and `while` condition `change > 1.e-3` has no effect. – kasper_341 Jun 20 '14 at 14:12
  • @kasper_341: See dragosht's answer above. That's why you should print `change` at the end of the loop every time, so you can see where the floating point error is occurring, because it's definitely occurring. My guess for where it was occurring was wrong, apparently. – wolfPack88 Jun 20 '14 at 14:21
0

The problem with your loop is that it's possible for none of the cases to match. Myself, I would write the last branch of the chain of ifs as

else {
    /* must be the case that change >= penny */
    change -= penny;
    printf("penny %.2f\n", penny);
}

The comment explains what should be the case here, but the program won't fail if it's not true.

If for some reason you needed to be sure that this comment was true (not an issue in this case, but in a more high-stakes context it might be), you might write either

else {
    assert(change >= penny);
    change -= penny;
    ...
}

or if you wanted to keep the structure you've got, you could write

else if(change >= penny)
{
    change -= penny;
    printf("penny %.2f\n", penny);
} else {
    /* ooops! This should be impossible */
    fprintf(stderr, "Can't happen: change %f < penny\n", change);
    exit(1);
}

or something like that. With a chain of if statements like that without a catch-all else, or with a switch without adefault`, you should get into the habit of automatically thinking "what happens if the impossible does happen?"

It's a larger issue (and possibly a bit of a distraction for just now), but programming with assertions is a really good habit to get into. If you can convince yourself that "it is impossible for X to be false" (such as pointer != NULL, or i < i_max) then write that as an assertion. If that assertion is ever false, then you've immediately found a major logic error in your code.

Final thing: why is this last if test failing? In mathematical arithmetic, it can't fail, but remember that computers use floating point numbers, in which 0.01 isn't precisely representable. Thus the number represented as 0.01 (or 0.05 or 0.10) isn't actually that, but some number about 1e-7 away from that, which means that the change value you have will be something either just below 0.01, or just above 0.0, and that's why both the change > 0.0 can be true, and change >= 0.01 false.

Norman Gray
  • 11,978
  • 2
  • 33
  • 56
  • i can see how adding last else statement catches when `something impossible happens` now output is `hi, how much do i owe u? .45 quarter 0.25 dime 0.10 nickel 0.05 penny 0.01 penny 0.01 penny 0.01 penny 0.01 Can't happen: change 0.01 < penny` – kasper_341 Jun 21 '14 at 14:56
  • why does it not print last `printf` `counter` statement ? – kasper_341 Jun 21 '14 at 15:04
  • Because after it prints "Can't happen..." it calls `exit(1)`, and... exits. If something happens that ‘can't happen’, the best thing is just to bail out (this is what the `assert()` macro/function does). – Norman Gray Jun 21 '14 at 18:01