C++ 在优化代码中提供不同答案的变量分组

C++ 在优化代码中提供不同答案的变量分组,c++,gcc,optimization,floating-point,floating-accuracy,C++,Gcc,Optimization,Floating Point,Floating Accuracy,我一直在尝试对我为大地坐标变换编写的C++类进行单元测试 我注意到,三个变量的微小分组变化会极大地影响函数中的错误 编辑:以下是可编译示例的完整函数: 假设纬度,经度和高度为零Earth::a=6378137和Earth::b=6356752.3我正在努力获取基准数据,今天工作中出现了一些问题,我不得不这样做 void Geodesy::Geocentric2EFG(double latitude, double longitude, double altitude, double *E, do

我一直在尝试对我为大地坐标变换编写的
C++
类进行单元测试

我注意到,三个变量的微小分组变化会极大地影响函数中的错误

编辑:以下是可编译示例的完整函数:

假设
纬度
经度
高度
为零
Earth::a=6378137
Earth::b=6356752.3
我正在努力获取基准数据,今天工作中出现了一些问题,我不得不这样做

void Geodesy::Geocentric2EFG(double latitude, double longitude, double altitude, double *E, double *F, double *G) {
    double a2 = pow<double>(Earth::a, 2);
    double b2 = pow<double>(Earth::b, 2);
    double radius = sqrt((a2 * b2)/(a2 * pow<double>(sin(latitude), 2) + b2 * pow<double>(cos(longitude), 2)));
    radius += altitude;

    *E = radius * (cos(latitude) * cos(longitude));
    *F = radius * (cos(latitude) * sin(longitude));
    *G = radius * sin(latitude);

    return;
}
有关守则:

*E = radius * cos(latitude) * cos(longitude);
*F = radius * cos(latitude) * sin(longitude);
产生的结果不同于:

*E = radius * (cos(latitude) * cos(longitude));
*F = radius * (cos(latitude) * sin(longitude));

编译器在优化级别为
3
gcc
中做了什么,以使这些结果
1e-2
不同?

您有不同的舍入,因为浮点不能代表所有数字:

a*b*c
(a*b)*c
,可能不同于
a*(b*c)

您可能也有类似的添加问题

添加的示例:

10e10f+1.f==10e10f

所以
(1.f+10e10f)-10e10f==10e10f-10e10f==0.f


1.f+(10e10f-10e10f)==1.f-0.f==1.f

使用汇编程序luke。试试gcc浏览器。(请注意,我在这里没有为不同的版本提供不同的汇编程序,但是ymmv)在编译w/o优化时会得到不同的结果吗?如果半径非常大,与sin/cos范围[-1,1]相比,那么期望不同的结果让我做一个基准,我会给你们回复,请给我们一个!代码仍然不符合SSCCE的要求。除此之外,请告诉我们您在哪里体验到这种行为。没有这一点,我们就没有机会复制它;我们只能猜测。这不能解释报告的行为,除非1e-2差异的报告值较大(
radius
必须较大)。在与正常值相乘的过程中,浮点
(a*b)*c
为某些舍入误差e0和e1精确生成a•b•(1+e0)•c•(1+e1),这些舍入误差的幅度最大为1/2 ULP(以舍入到最近的模式),而
a*(b*c)
为某些误差e2和e3精确生成a•(b•c•(1+e2))•(1+e3)。当e0和e1为+1/2 ULP,e2和e3为-1/2 ULP时,它们之间的最大差异会出现,除非
半径
较大,否则差异不会为1e-2。如果
半径
是地球的半径(以米为单位),并且代码使用32位浮点,则可能会出现这种情况,我们看到
radius
为6378137,正在使用
double
。因此,
(a*b)*c
a*(b*c)
之间的差异不能解释观察到的1e-2误差。
*E = radius * (cos(latitude) * cos(longitude));
*F = radius * (cos(latitude) * sin(longitude));