Eigen 为什么两个类似的浮点计算会给出两个不同的结果?

Eigen 为什么两个类似的浮点计算会给出两个不同的结果?,eigen,floating,floating-point-precision,Eigen,Floating,Floating Point Precision,下面的代码通过将特征向量仅用作容器或简单的C数组来实现相同的计算。它产生一个闭合的但不是位到位等效的结果 最后的数学运算是x*alpha+y*beta #包括 int main() { 本征::矢量xd x(2); 双精度*y=新双精度[2]; 长-长整数a=4603016991731078785; 双ga=*(双*)(&a); 长整型b=-4617595986472363966; 双gb=*(双*)(&b); 长整型x0=451; 长整型x1=-9223372036854775100; x[0

下面的代码通过将特征向量仅用作容器或简单的C数组来实现相同的计算。它产生一个闭合的但不是位到位等效的结果

最后的数学运算是
x*alpha+y*beta

#包括
int main()
{
本征::矢量xd x(2);
双精度*y=新双精度[2];
长-长整数a=4603016991731078785;
双ga=*(双*)(&a);
长整型b=-4617595986472363966;
双gb=*(双*)(&b);
长整型x0=451;
长整型x1=-9223372036854775100;
x[0]=*(双*)(&x0);
y[0]=*(双*)(&x0);
x[1]=*(双*)(&x1);
y[1]=*(双*)(&x1);
双r=ga*x[0]+gb*x[1];
双s=ga*y[0]+gb*y[1];
}
为什么会这样


使用MSVC和gcc(64位操作系统)时,结果会有所不同。

这可能是因为一个计算完全在FPU(浮点单元)内完成,精度为80位,而另一个计算部分使用64位精度(双精度的大小)。这也可以在不使用特征值的情况下进行演示。请看以下程序:

intmain()
{
//将ga、gb、y[0]和y[1]加载到原始程序中
双精度*y=新双精度[2];
长-长整数a=4603016991731078785;
双ga=*(双*)(&a);
长整型b=-4617595986472363966;
双gb=*(双*)(&b);
长整型x0=451;
长整型x1=-9223372036854775100;
y[0]=*(双*)(&x0);
y[1]=*(双*)(&x1);
//在原始程序中计算s
双s=ga*y[0]+gb*y[1];
//相同的计算,但分步骤进行
双r1=ga*y[0];
双r2=gb*y[1];
双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位。这可能是因为浮点堆栈没有在函数调用中保留。

这可能是因为一个计算完全在FPU(浮点单元)内完成,精度为80位,而另一个计算部分使用64位精度(双精度的大小)。这也可以在不使用特征值的情况下进行演示。请看以下程序:

intmain()
{
//将ga、gb、y[0]和y[1]加载到原始程序中
双精度*y=新双精度[2];
长-长整数a=4603016991731078785;
双ga=*(双*)(&a);
长整型b=-4617595986472363966;
双gb=*(双*)(&b);
长整型x0=451;
长整型x1=-9223372036854775100;
y[0]=*(双*)(&x0);
y[1]=*(双*)(&x1);
//在原始程序中计算s
双s=ga*y[0]+gb*y[1];
//相同的计算,但分步骤进行
双r1=ga*y[0];
双r2=gb*y[1];
双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中,即multip