C:双精度算术不精确

时间:2016-09-14 02:24:13

标签: c double

我有以下C代码:

int main()
{
    double a=.1,b=.2,c=.3,d=.4;
    double e=a+b+c;
    if (d- (1-e)){printf("not equal\n");}
    printf("%.20f\n",d-(1-e));
}

我得到的结果是:

not equal
0.00000000000000011102

我知道这是由于计算机节省双倍的方式引起的不精确。有没有办法解决这个问题,并使 d-(1-e)等于0?

2 个答案:

答案 0 :(得分:1)

正如PRP正确建议:您需要设置一个小数字以用作零。标准C库(C标准中的附件F)为此目的在float.h中提供了一些宏。您可以像使用它们一样使用它们:

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

int main()
{
  double a = .1, b = .2, c = .3, d = .4;
  double e = a + b + c;
  if (d - (1 - e)) {
    printf("not equal\n");
  }
  printf("%.20f\n", d - (1 - e));
  printf("%.20f\n", DBL_EPSILON);
  if (fabs(d - (1 - e)) <= DBL_EPSILON) {
    printf("equal\n");
  }
  exit(EXIT_SUCCESS);
}

答案 1 :(得分:0)

此处的问题是,.1.2.3不是基本2中的有限数字,而是二进制表示中的周期性数据。

我将尝试说明:假设我们正在尝试使用基数为3的编号系统,并尝试求和.1(三分之一)+ .1(另外三分之一)和{{1 (这是另外的第三个),但不是在基数3中这样做,而是在基数10中进行。

在基数3中

+ .1,将基数为10转换为.1(16个基数为十位数),如果您尝试将其添加三次,则会获得.3333333333333333和不是.9999999999999999

这里不可能解决这个问题,但切断结果的准确性。如果我们有10个数字,并尝试添加1.0三次......我们将获得.3333333333(十位数字),这里的解决方案是将结果剪切为九位并围绕它(在这种情况下.9999999999),但如果您这个数字减去到正确的数字(1.000000000),您将再次1.000000000 - 0.9999999999

这在浮点算术中是常见的,问题是浮点数本质上是离散的,而不是像实数那样连续。

正如已经指出的那样,0.0000000001头文件有常量来处理这个问题,但是你必须小心使用它们,因为它们会引发你的新错误:

<limits.h>是浮点常量,它从DBL_EPSILON移动到不同于1.0的下一个数字,并且两者之间没有浮点数。在我们的情况下,它应该是,因为我们使用基数10和10位数,数字应该是(1.0)由于数字基数不同于10,这个数字不是一个整数,如在10中

这个数字相对于1,所以如果你减去两个数字,那么你的epsilon应该是相对于这些数字中最大的数字

1.000000001 - 1.000000000 => 0.00000001

但在实际代码中,由于您通常使用多个总和和减法,您将积累所有这些舍入误差,很容易超出此容差,您可以使用if (fabs(actual - correct) < DBL_EPSILON*fabs(correct)) { /* consider both results equal */ } 2.0次这个值。

浮点编号系统在IEEE-754规范中有更好的描述。你会发现那里DBL_EPSILON不是你必须考虑使两个浮点相等的唯一常数(当你接近10.0数字更多时,当你使用更大的数字时,它们更稀疏。)