3

我理解是这样的:在C双精度减去2个double数字时++它们首先转化为开始与一个次2到幂的一个有效数。那么如果相减的数字在有效数字中具有相同的指数和许多相同的数字,则可以得到错误,从而导致精度的损失。为了测试这个对我的代码,我写了下面的安全增加功能:C++浮点减法误差和绝对值

double Sadd(double d1, double d2, int& report, double prec) { 
    int exp1, exp2; 
    double man1=frexp(d1, &exp1), man2=frexp(d2, &exp2); 
    if(d1*d2<0) { 
     if(exp1==exp2) { 
      if(abs(man1+man2)<prec) { 
       cout << "Floating point error" << endl; 
       report=0; 
      } 
     } 
    } 
    return d1+d2; 
} 

然而,测试此我注意到一些奇怪的事情:看来,实际的错误(不是函数是否报告错误,但是从实际的一个产生计算)似乎取决于减去数字的绝对值,而不是在尾数相同的数字只是数...

有关示例,使用1e-11的精度prec并减去以下号码:

1)9.8989898989898-9.8989898989897:The函数报告错误,我得到了非常不正确的值9.9475983006414e-14

2)98989898989898-98989898989897:函数报告错误,但我得到正确的值1

显然我误解的东西。有任何想法吗?

+0

如果您希望获得的不仅是一个近似结果,而且是一个浮点计算错误的界限,您可以使用区间算术,向下舍入为下限,向上舍入为上限。必须有C++包才能做到这一点。 – 2013-04-29 20:40:02

回答

6

如果减去这几乎是相等的两个浮点值,结果将主要体现在低位噪音。这里差不多相同的指数和几乎相同的数字。例如,1.0001和1.0000几乎相等,减去它们可能会被这样的测试所捕获。但1.0000和0.9999的差别完全相同,并且不会被像这样的测试所捕获。

而且,这是不是安全的新增功能。相反,这是对设计/编码错误的事后检查。如果你减去两个非常接近的值,那么噪音就会造成错误。修复错误。我不反对使用类似这样的东西作为调试帮助,但请将其称为暗示这就是它的意思,而不是暗示浮点加法存在固有的危险。此外,把除了函数内部的检查似乎过度:断言这两个值不会造成问题,其次是一个普通的老浮点加法,可能会更好。毕竟,代码中的大部分新增内容都不会导致问题,并且您最好知道问题所在的位置;在问题点上发表声明。

+1

如果你写了“如果你减去近似相等的两个**近似值**浮点值,结果将主要反映低位噪声”,那么没有人会狡辩。你写的方式,有人可能会指出,减去两个几乎相等的浮点值是一个确切的操作(Sterbenz引理)。 – 2013-04-29 15:10:47

+0

@PascalCuoq - 我故意有点模糊,因为这是写给初学者,他不会从浮点数学的论文中受益。 – 2013-04-29 15:14:10

+0

@PeteBecker谢谢!我现在看到,我应该以某种方式使用数字的二进制值来完成此操作。你是否同意那么测试会起作用,从那以后,有效数字总是从1开始的?对于名称:你是对的,我想要的是一个用于查找代码中的问题的工具;安全的补充是错误的选择。 – jorgen 2013-04-29 15:21:55

2

+1皮特贝克尔的答案。

例如,如果你减去

1.0-0.99999999999999 

所以,

bool degenerated = 
     (epx1==exp2 && abs(d1+d2)<prec) 
    || (epx1==exp2-1 && abs(d1+2*d2)<prec) 
    || (epx1==exp2+1 && abs(2*d1+d2)<prec); 

可以省略检查

注意,也可能与EXP1发生退化导致的问题!= EXP2为d1 * d2 < 0,否则保持它避免整个测试,否则...

如果您也想处理的精度退化规格化花车损失,那将是更复杂(这是因为如果尾数有较少位)位。

+0

好吧,我明白了!是的,d1 * d2在那里,所以我不必知道复杂代码中的符号,但我应该把它放在开始,所以一切都被避免,否则。 – jorgen 2013-04-30 15:09:53

1

这很容易证明对于IEEE 754浮点运算,如果x/2 < = y < = 2x那么计算x-y就是一个精确的运算,并且会给出准确的结果而没有任何舍入误差。

而且如果加法或减法的结果是非规格化数,那么结果总是总是确切的。