5

我是编程世界的新手,而 C++ 已经让我失望了。我的程序中有这些行

pennies=(amount-nickels*.05)/.01;

金额是 a double,而 和镍币int

pennies是 a时,程序会返回正确的值,但只要pennies是a 时double,事情就会有一点偏差(大部分时间相差 1)。int

为什么会这样?

4

4 回答 4

8

发生这种情况是因为该值被截断,从而丢失了小数精度。

对于整数:

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;
于 2013-08-06T07:43:51.727 回答
3

正如其他人指出的那样,问题在于机器浮点0.10.05没有精确的表示。这里最简单的解决方案是简单地对结果进行四舍五入,而不是截断:

pennies = round( (amount - nickels * 0.05) / 0.01 );

在这种特殊情况下(您正在处理硬币)更好的解决方案是简单地在任何地方使用整数,并以美分而不是美元来做所有事情。然而,这很快就会停止工作(或需要一些非常复杂的编程):即使是计算增值税或销售税之类的事情也会导致问题。对于这种情况,有两种可能的解决方案:

  • 继续使用double, 根据需要四舍五入。这是最简单和最通用的解决方案。但是请注意,使用它时,会对二进制值进行四舍五入,并且在某些情况下,它可能与四舍五入的十进制值不同。差异应该足够小,以至于对于日常使用来说并不重要,但是大多数国家/地区都有法律规定这些值必须如何四舍五入,并且这些四舍五入规则始终以十进制指定。这意味着该解决方案不能用于满足法律要求的情况(例如计算销售税、公司簿记等)。

  • 使用某种十进制类。网络上有一个可用的号码。这将导致执行时间变慢。(通常,谁在乎。你会在 100 微秒内得到结果,而不是 10 微秒。但也有例外;例如在进行蒙特卡洛模拟时。)

此外,您必须注意何时涉及较大的值。对于您的个人财务,它几乎可以肯定是无关紧要的,但 int确实有一个最大值,并且有一个点 double不能再正确地表示整数值。

当然,如果这只是一个有助于学习 C++ 的练习,只需使用roundor 整数,其余的不要太担心。

至于您的评论“C++ 已经失去了我”:这与 C++ 本身无关。您会在(几乎)任何编程语言中遇到完全相同的问题。

于 2013-08-06T09:03:17.993 回答
1
                   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 将被评估,由于与双精度值相加,它将被提升为双精度,然后分母也转换为双精度,因为分子是双精度,因此计算仅以双精度格式发生.

于 2013-08-06T09:19:10.723 回答
1

您在表达式的右侧计算一个浮点数,然后尝试将一个双精度值存储在一个 int 值中。执行此操作时,小数部分会丢失。

于 2013-08-06T07:53:45.600 回答