最近我分析了一段旧代码,使用VS2005编译。由于“debug”(未进行优化)和“release”(/O2 /Oi /Ot选项)编译中存在不同的数值行为,因此我需要进行分析。这段(简化过的)代码如下:
void f(double x1, double y1, double x2, double y2)
{
double a1, a2, d;
a1 = atan2(y1,x1);
a2 = atan2(y2,x2);
d = a1 - a2;
if (d == 0.0) { // NOTE: I know that == on reals is "evil"!
printf("EQUAL!\n");
}
如果使用相同的值对函数f(例如f(1,2,1,2))进行调用,则预期函数f应打印“EQUAL”,但是在“release”中并不总是发生这种情况。实际上,编译器已将代码优化为类似于d = a1-atan2(y2,x2)的形式,并完全删除了对中间变量a2的赋值。此外,它利用了第二个atan2()结果已经在FPU堆栈上的事实,因此重新加载FPU上的a1并减去了值。问题在于FPU以扩展精度(80位)工作,而a1仅为双精度(64位),因此将第一个atan2()结果保存在内存中实际上失去了精度。最终,d包含扩展和双精度之间的“转换错误”。
我知道应该避免使用浮点/双精度的等号(==运算符)。我的问题不在于如何检查双精度之间的接近程度。我的问题是关于局部变量赋值的“契约性”。按照我的“天真”观点,赋值应强制编译器将值转换为变量类型所表示的精度(在我的情况下为double)。如果变量是“float”,会怎样?如果它们是“int”(奇怪,但合法)呢?
简而言之,C标准对这些情况有什么规定?
FLT_EVAL_METHOD
的值。 - chux - Reinstate MonicaFLT_EVAL_METHOD
。而且,并不是所有编译器都会按照它们所定义的方式执行(https://dev59.com/pmMm5IYBdhLWcg3wX-HT)。 - Pascal Cuoqx
仅用作表达式x + 1
的一部分,则永远不能将x
分配,而应直接计算x + 1
吗? - Pascal Cuoqvolatile
变量,那是另一回事)。 - Pascal Cuoq