3

我理解它的方式是:double在 C++ 中用双精度减去两个数字时,它们首先被转换为一个有效数字,从 2 的指数幂开始。如果减去的数字具有相同的指数并且有效数字中有许多相同的数字,则可能会出现错误,从而导致精度损失。为了测试我的代码,我编写了以下安全添加函数:

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:函数报告错误,我得到高度不正确的值9.9475983006414e-14

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

显然我误解了一些东西。有任何想法吗?

4

3 回答 3

6

如果减去两个几乎相等的浮点值,结果将主要反映低位中的噪声。这里的几乎相等不仅仅是相同的指数和几乎相同的数字。例如,1.0001 和 1.0000 几乎相等,减去它们可能会被这样的测试捕获。但是 1.0000 和 0.9999 相差完全相同的量,并且不会被这样的测试捕获。

此外,这不是安全的添加功能。相反,它是对设计/编码错误的事后检查。如果你减去两个非常接近以至于噪声很重要的值,你就犯了一个错误。修正错误。我不反对使用这样的东西作为调试辅助工具,但请称它为暗示它就是这样的东西,而不是暗示浮点加法存在固有的危险。此外,将检查放在加法函数中似乎过分:断言这两个值不会引起问题,然后是普通的旧浮点加法,可能会更好。毕竟,你代码中的大部分添加不会导致问题,你最好知道问题点在哪里;

于 2013-04-29T15:08:17.343 回答
2

+1 皮特·贝克尔的回答。

注意exp1!=exp2也可能出现退化结果的问题

例如,如果你减去

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);

您可以省略对 d1*d2<0 的检查,或者保留它以避免整个测试,否则...

如果您还想处理退化的非规范化浮点数的精度损失,那将涉及更多(就好像有效位的位数更少)。

于 2013-04-30T09:48:06.340 回答
1

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

如果加法或减法的结果是一个非规范化的数字,那么结果总是准确的。

于 2014-06-21T12:41:39.223 回答