我有以下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?
答案 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中进行。
+ .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
数字更多时,当你使用更大的数字时,它们更稀疏。)