C中的数字精度错误

时间:2012-11-23 08:30:00

标签: c precision floating-point-precision

这是我写的代码:

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

int main()
{
    double num;
    int tmp;
    printf("enter a number!\n");
    scanf("%lf",&num);
    tmp=num*10000;
    printf(" temp=%d\n",tmp);

    return 0; 
}

当我输入数字1441.1441时,我得到的结果是14411440而不是 14411441这个输入数乘以10000后显然是正确的结果。有人可以帮我解决这个问题吗?

4 个答案:

答案 0 :(得分:10)

由于绝大多数实数无法准确表示,您可能会发现1441.1441实际上存储为1441.14409999_blah_blah_blah之类的内容。你可以通过插入来找到它:

printf ("%.50lf\n", num);

scanf后立即看到(删除了尾随零):

1441.14409999999998035491444170475006103515625

现在,根据您的输入,这实际上是正确的(即最接近的)值。那里的下一个最高数字给你:

1441.144100000000207728589884936809539794921875

第一个值的错误是:

0.00000000000001964508555829524993896484375
               ^ ~ 2 x 10^-14

而第二个错误是:

0.000000000000207728589884936809539794921875
              ^ ~ 2 x 10^-13

你可以看到后者的错误大约是后者的10倍。

当您将其乘以10000并尝试将其变为int时,它会被舍入向下(截断)。那是因为(C11)标准在6.3.1.4

中有这个说法
  

当实数浮点类型的有限值转换为_Bool以外的整数类型时,小数部分被丢弃(即,该值被截断为零)。

您可以尝试的一件事就是将您的鞋带线改为:

tmp = num * 10000 + 0.5;

有效地将截断转换为舍入操作。我认为适用于所有情况,但您可能需要对其进行测试(并密切注意)以防万一。

答案 1 :(得分:1)

对于一般原则,paxdiablo's answer包含相关部分。大多数终止小数部分不能精确表示为二进制浮点数,因此浮点变量的值略小于或大于给定字符串中数字表示的数学值,因此当您想要获得适当的整数时缩放后的值,你应该舍入而不是截断。

但在这里的具体例子中,我们有不同的场景。最接近IEEE754双精度(64位二进制)值到1441.1441是

1441.14409999999998035491444170475006103515625

确实比1441.1441小一点。但如果该值乘以10000作为IEEE754双精度值,则结果正好

14411441

这里发生的是,按照5.2.4.2.2第9段的规定允许的

  

除了赋值和强制转换(删除所有额外的范围和精度)之外,具有浮动操作数的运算符产生的值以及通常算术转换和浮动常量的值将被计算为范围和精度的格式可能比类型所要求的要大。

(强调我的),产品的评估精度高于类型所需的精度(可能是x87 80位格式),产生的值略小,当乘法结果转换为{{1 ,小数部分被丢弃,你得到14411440。

int

该值存储在scanf("%lf",&num); 中,因此它必须具有num的精确度。

double

产品tmp=num*10000; 既不会存储也不会投射到num * 10000,因此它可能具有更高的精度,从而产生比最接近的double值更小或更大的值。然后截断该值以获得double

如果您将产品存储在int变量

double

或在转换为num *= 10000; tmp = num; 之前将其投放到double

int

你应该得到输入tmp = (double)(num * 10000); 的结果14411441(但请注意,并非所有编译器都始终遵守在转换或存储时转换为精确所需精度的要求 - 违反标准 - 因此无法保证这将产生14411441与所有优化设置)。

由于许多64位平台使用SSE指令而不是x87协处理器执行浮点运算,因此观察到的行为不太可能出现在64位系统上而不是32位系统上。

答案 2 :(得分:0)

尽量让它像那样:

float a = 3.14;

int i = (int)(a+0.5);

在你的情况下:

 double num;
 int tmp;
 printf("enter a number!\n");
 scanf("%lf",&num);
 tmp=(int)(num*10000 + 0.5);
 printf(" temp=%d\n",tmp);

答案 3 :(得分:-4)

看起来scanf在scanf中使用float精度。我简单地检查了1441.1441在浮动中的表示为1441.1440。 在genereal中,你不应该依赖于浮点运算的精度。