while循环不终止 – 浮点运算

我正在使用浮点值处理C程序,下面是我的代码。

#include #include #include 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; } 

输出是:

 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` 

我必须按ctrl c才能终止循环

最后的printf("your count is %i\n", counter); 根本不执行 – count # of coins使用count # of coins

如果我用int替换float类型,它可以正常工作。

请帮助解决这个问题

在循环开始时添加:

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

将发出此输出:

 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 

这是由内部浮点数表示引起的。 有一个固有的错误 - 大多数浮点数没有精确的表示 - 它们只是非常好的近似值。

做一个巨大的帮助。 取输入的值,乘以100,然后以美分而不是美元计算所有计算。 只有您的打印输出才能转换为美元和美分。

计算机使用整数比使用浮点数更准确。

在循环结束时打印出change ,并查看最终的变化。 我的猜测是,由于浮点错误,它将是一个非常小的正数。 您可以通过简单地将while语句change > 1.e-3change > 1.e-3或其他更小的数字来解决此问题。

你的循环的问题是没有一个案例可以匹配。 我自己,我会写ifs链的最后一个分支

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

评论解释了这里应该是什么情况,但如果不是这样,程序将不会失败。

如果由于某种原因你需要确定这个评论是真的(在这种情况下不是问题,但在更高风险的情况下可能是这样),你可能会写

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

或者如果你想保留你所拥有的结构,你可以写

 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); } 

或类似的东西。 if有一系列if语句没有else所有的else ,或者switch without a默认的switch without a ,你应该养成自动思考“如果不可能发生会怎么样?”的习惯。

这是一个更大的问题(并且可能有点分散注意力),但使用断言进行编程是一个非常好的习惯。 如果你能说服自己“X不可能是假的”(比如pointer != NULL ,或者i < i_max )那么把它写成断言。 如果该断言是错误的,那么您立即在代码中发现了一个主要的逻辑错误。

最后一件事: if测试失败, 为什么这是最后一次? 在数学运算中,它不会失败,但要记住计算机使用浮点数,其中0.01不能精确表示。 因此,表示为0.01(或0.05或0.10)的数字实际上不是那个数字,而是距离数字大约1e-7的数字,这意味着您所具有的change值将低于0.01或略高于0.0, 这就是为什么change > 0.0都可以为真,并且change >= 0.01 false。