问题
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
回答1:
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.
回答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.
回答3:
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.
回答4:
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 a
default`, 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.
来源:https://stackoverflow.com/questions/24328939/while-loop-does-not-terminate-float-arithmetic