C++ 在「;“双”字;C语言中的数据挖掘与优化
我最近分析了一段使用VS2005编译的旧代码,因为“调试”(无优化)和“发布”(/O2/Oi/Ot选项)编译中的数值行为不同。(简化的)代码如下所示:C++ 在「;“双”字;C语言中的数据挖掘与优化,c++,c,visual-studio,floating-point,C++,C,Visual Studio,Floating Point,我最近分析了一段使用VS2005编译的旧代码,因为“调试”(无优化)和“发布”(/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
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(1,2,1,2)
)调用函数f
,则预期函数f
将打印“EQUAL”,但这并不总是发生在“release”中。事实上,编译器对代码进行了优化,就像它是d=a1-atan2(y2,x2)
一样,并且完全删除了对中间变量a2
的赋值。此外,它还利用了第二个atan2()
的结果已经在FPU堆栈上的事实,因此在FPU上重新加载a1
,并减去值。问题是,FPU以扩展精度(80位)工作,而a1
是“仅”双精度(64位),因此将第一个atan2()
的结果保存在内存中实际上会丢失精度。最后,d
包含扩展精度和双精度之间的“转换错误”
我非常清楚,应该避免使用float/double标识(=
运算符)。我的问题不是如何检查双打之间的距离。我的问题是关于如何考虑局部变量的“契约”赋值。根据我“天真”的观点,赋值应该强制编译器将值转换为变量类型所表示的精度(在我的例子中是double)。如果变量是“浮动”的呢?如果它们是“int”(奇怪但合法)呢
那么,简言之,C标准对这种情况怎么说
根据我“天真”的观点,赋值应该强制编译器将值转换为变量类型所表示的精度(在我的例子中是double)
是的,这就是C99标准所说的。见下文
那么,简言之,C标准对这种情况怎么说
在某些情况下,C99标准允许以比类型所暗示的更高的精度计算浮点运算:查找FLT\u EVAL\u METHOD
和FP\u CONTRACT
。在中,这两种结构与超精度相关。但我不知道有什么词可以解释为允许编译器任意降低浮点值的精度,从计算精度降低到类型精度。严格按照标准的解释,这种情况只会以确定的方式发生在特定的地点,如作业和演员阵容
最好是阅读与FLT\u EVAL\u方法相关的部分:
C99允许在超出范围和精度的情况下进行评估
某些规则。这些在5.2.4.2.2第8段中概述:
除了赋值和强制转换(删除所有额外范围和
精度),使用浮点操作数和
需要进行常规算术转换和浮点运算的值
常数的计算格式可能会影响其范围和精度
大于类型所需的值。评价的使用
格式以实现定义的值为特征
飞行评估方法:
Joseph S.Myers接着描述了GCC中的情况,随后发布了他的文章。情况和编译器(以及无数其他编译器)中的情况一样糟糕:
当使用x87浮点时,GCC将FLT_EVAL_方法定义为2。它的
但是,实施不符合C99对
FLT_EVAL_METHOD==2,因为它是由后端实现的
假装处理器支持SFmode和上的操作
DFmode:
- 有时,根据优化情况,值可能会溢出到
内存处于SFmode或DFmode模式,因此无法预测会丢失多余的精度
而不是在C99指定它丢失的地方
- 赋值通常不会失去多余的精度,但
-ffloat store
可能会使它更有可能这样做
< > C++标准继承了C99的代码<数学> h <代码>,和<代码>数学.h <代码>是定义<代码> FLTYEVALILY方法< /代码>的标头。出于这个原因,你可能会期望C++编译器遵循这一点,但他们似乎并没有认真对待这个问题。即使是G++也不支持-fexcess precision=standard
,尽管它使用了与GCC相同的后端(自从Joseph S.Myers的文章和附带的补丁以来,GCC就支持此选项)。我认为该标准没有承诺对库中包含的函数进行浮点返回。但这是我对这个主题的理解,所以不是很有帮助。默认情况下,VisualStudio将精度设置为“精确”,这允许它进行此类优化。你可以试着将它设置为strict,看看会发生什么。如果其他方法都失败了,那么将所有的中间产物都转换为double就可以了。至少在我尝试为一个非常特定的操作获取IEEE特定的行为时,它起了作用,我不想启用全局fp:strict。如果您想消除x87的多余精度,通常会有一个特定的编译标志;在gcc上有-fexcess precision=standard
(和-ffloat store
),用于VC++/fp:strict
@chux VS 2005甚至没有声明实现C99,因此它不必定义FLT_EVAL_方法
。而且这并不是说编译器在定义它时总是照他们说的做()