我的理解是:在 C++ 中用 double 减去两个 double
数字时,它们首先转换为以 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
显然我误解了一些东西。有什么想法吗?
最佳答案
如果您减去两个几乎相等的浮点值,结果将主要反射(reflect)低位中的噪声。这里几乎相等不仅仅是相同的指数和几乎相同的数字。例如,1.0001 和 1.0000 几乎相等,减去它们可能会被这样的测试捕获。但是 1.0000 和 0.9999 的差值完全相同,不会被这样的测试捕获。
此外,这不是一个安全的加法函数。相反,它是对设计/编码错误的事后检查。如果你减去两个非常接近以至于噪音很重要的值,你就犯了一个错误。修正错误。我不反对使用类似这样的东西作为调试辅助工具,但请称它为暗示它就是这样的东西,而不是暗示浮点加法本身存在危险。此外,将检查放在加法函数中似乎过分:断言这两个值不会导致问题,然后进行普通的旧浮点加法,可能会更好。毕竟,代码中的大部分添加都不会导致问题,您最好知道问题点在哪里;将断言放在问题点。
关于c++浮点减法错误和绝对值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16281685/