以下代码通过使用特征向量作为仅容器或简单的C数组来实现相同的计算。它产生一个封闭但不是位到位的等效结果。
最后的数学运算是x * alpha + y * beta
。
#include <Eigen/Eigen>
int main()
{
Eigen::VectorXd x(2);
double* y = new double[2];
long long int a = 4603016991731078785;
double ga = *(double*)(&a);
long long int b = -4617595986472363966;
double gb = *(double*)(&b);
long long int x0 = 451;
long long int x1 = -9223372036854775100;
x[0] = *(double*)(&x0);
y[0] = *(double*)(&x0);
x[1] = *(double*)(&x1);
y[1] = *(double*)(&x1);
double r = ga*x[0] + gb*x[1];
double s = ga*y[0] + gb*y[1];
}
为什么会这样?
使用MSVC和gcc(64位操作系统)时结果不同。
答案 0 :(得分:1)
这可能是因为一个计算完全在FPU(浮点单元)内完成,具有80位精度,而另一个计算使用部分64位精度(双倍大小)。这也可以在不使用Eigen的情况下进行演示。看看下面的程序:
int main()
{
// Load ga, gb, y[0], y[1] as in original program
double* y = new double[2];
long long int a = 4603016991731078785;
double ga = *(double*)(&a);
long long int b = -4617595986472363966;
double gb = *(double*)(&b);
long long int x0 = 451;
long long int x1 = -9223372036854775100;
y[0] = *(double*)(&x0);
y[1] = *(double*)(&x1);
// Compute s as in original program
double s = ga*y[0] + gb*y[1];
// Same computation, but in steps
double r1 = ga*y[0];
double r2 = gb*y[1];
double r = r1+r2;
}
如果你在没有优化的情况下编译它,你会看到r和s有不同的值(至少,我在我的机器上看到过)。查看汇编代码,在第一次计算中,将ga,y [0],gb和y [1]的值加载到FPU中,然后计算ga * y [0] + gb * y [1]为完成后,结果存储在内存中。 FPU以80位进行所有计算,但是当结果存储在内存中时,数字被舍入,以便它适合双变量的64位。
第二次计算的方式不同。首先,将ga和y [0]加载到FPU中,相乘,然后舍入为64位数并存储在内存中。然后,将gb和y [1]加载到FPU中,相乘,然后舍入为64位数并存储在内存中。最后,将r1和r2加载到FPU中,添加,舍入为64位数并存储在内存中。这一次,计算机对中间结果进行了舍入,这导致了差异。
对于此计算,舍入具有相当大的影响,因为您正在使用非正规数。
现在,这里有一点我不太确定(如果这是你的问题,我道歉):这与原始程序有什么关系,其中x是一个特征容器?这里的计算如下:来自Eigen的函数被调用以获得x [0],然后ga和来自该函数的结果被加载到FPU中,相乘并存储在临时存储器位置(64位,所以这是圆)。然后将gb和x [1]加载到FPU中,相乘,加到存储在临时存储单元中的中间结果中,最后存储在x中。因此,在原始程序中的r的计算中,ga * x [0]的结果被舍入为64位。也许这样做的原因是浮点堆栈不会在函数调用中保留。