我是编程世界的新手,而 C++ 已经让我失望了。我的程序中有这些行
pennies=(amount-nickels*.05)/.01;
金额是 a double
,而 和镍币是int
。
当pennies是 a时,程序会返回正确的值,但只要pennies是a 时double
,事情就会有一点偏差(大部分时间相差 1)。int
为什么会这样?
我是编程世界的新手,而 C++ 已经让我失望了。我的程序中有这些行
pennies=(amount-nickels*.05)/.01;
金额是 a double
,而 和镍币是int
。
当pennies是 a时,程序会返回正确的值,但只要pennies是a 时double
,事情就会有一点偏差(大部分时间相差 1)。int
为什么会这样?
发生这种情况是因为该值被截断,从而丢失了小数精度。
对于整数:
int i;
i = 1.1 //stores 1 in i
i = 1.2 //stores 1 in i
i = 1.3 //stores 1 in i
i = 1.4 //stores 1 in i
i = 1.5 //stores 1 in i
i = 1.6 //stores 1 in i
i = 1.7 //stores 1 in i
i = 1.8 //stores 1 in i
i = 1.9 //stores 1 in i
i = 1.999999 //stores 1 in i
我建议您将表达式更改为:
pennies=amount*100-nickels*5;
正如其他人指出的那样,问题在于机器浮点0.1
并
0.05
没有精确的表示。这里最简单的解决方案是简单地对结果进行四舍五入,而不是截断:
pennies = round( (amount - nickels * 0.05) / 0.01 );
在这种特殊情况下(您正在处理硬币)更好的解决方案是简单地在任何地方使用整数,并以美分而不是美元来做所有事情。然而,这很快就会停止工作(或需要一些非常复杂的编程):即使是计算增值税或销售税之类的事情也会导致问题。对于这种情况,有两种可能的解决方案:
继续使用double
, 根据需要四舍五入。这是最简单和最通用的解决方案。但是请注意,使用它时,会对二进制值进行四舍五入,并且在某些情况下,它可能与四舍五入的十进制值不同。差异应该足够小,以至于对于日常使用来说并不重要,但是大多数国家/地区都有法律规定这些值必须如何四舍五入,并且这些四舍五入规则始终以十进制指定。这意味着该解决方案不能用于满足法律要求的情况(例如计算销售税、公司簿记等)。
使用某种十进制类。网络上有一个可用的号码。这将导致执行时间变慢。(通常,谁在乎。你会在 100 微秒内得到结果,而不是 10 微秒。但也有例外;例如在进行蒙特卡洛模拟时。)
此外,您必须注意何时涉及较大的值。对于您的个人财务,它几乎可以肯定是无关紧要的,但
int
确实有一个最大值,并且有一个点
double
不能再正确地表示整数值。
当然,如果这只是一个有助于学习 C++ 的练习,只需使用round
or 整数,其余的不要太担心。
至于您的评论“C++ 已经失去了我”:这与 C++ 本身无关。您会在(几乎)任何编程语言中遇到完全相同的问题。
pennies=(amount-nickels*.05)/.01;
在上面的表达式中,分子和分母算术发生在 double因为变量数量(它是 double)-隐式转换发生在这里。最后,如果 pennies 是 double,则计算结果将存储在 pennies 中,如果是 int,则计算结果的小数部分将丢失。中间计算总是以双倍发生,而且它不依赖于便士类型。
例子
#include <stdio.h>
int main()
{
int a ;
double c,d=10.55555;
a = c= (d+ 10*.5)/0.5 ;
printf("%f : %d/n",c,a);
return 0;
}
输出是:
31.111100 : 31
这里首先 10 被提升为浮点数,然后 10*.5 将被评估,由于与双精度值相加,它将被提升为双精度,然后分母也转换为双精度,因为分子是双精度,因此计算仅以双精度格式发生.
您在表达式的右侧计算一个浮点数,然后尝试将一个双精度值存储在一个 int 值中。执行此操作时,小数部分会丢失。