C语言中“ double”运算和优化

朱塞佩·圭里尼(Giuseppe Guerrini)

我最近分析了用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 "evil"!
   printf("EQUAL!\n");
}

f如果使用相同的值对(例如f(1,2,1,2)调用该函数,该函数将显示“ EQUAL” ,但这并不总是在“ release”中发生。确实发生过这样的情况,编译器已经像优化代码一样优化了代码,d = a1-atan2(y2,x2)并完全删除了对中间变量的分配a2此外,它利用了以下事实的优势:secondatan2()的结果已经在FPU堆栈上,因此将其重新加载a1到FPU上并减去了值。问题在于FPU以a1“仅”双精度(64位)的双精度(80位)工作,因此将firstatan2()的结果保存在内存中实际上失去了精度。最终,d包含扩展精度和双精度之间的“转换错误”。

我完全知道==应该避免使用float / double标识(运算符)。我的问题不是关于如何检查双打之间的接近度。我的问题是,应如何考虑对本地变量进行“合约式”分配。以我的“天真”观点,赋值应该强制编译器将值转换为由变量类型表示的精度(在我的情况下为double)。如果变量是“ float”,该怎么办?如果它们是“ int”(奇怪,但合法)怎么办?

简而言之,对于这种情况,C标准怎么说?

帕斯卡·库克(Pascal Cuoq)

以我的“天真”观点,赋值应该强制编译器将值转换为由变量类型表示的精度(在我的情况下为double)。

是的,这就是C99标准所说的。见下文。

简而言之,对于这种情况,C标准怎么说?

在某些情况下,C99标准允许以比该类型隐含的精度更高的精度来计算浮点运算:look FLT_EVAL_METHODandFP_CONTRACT在该标准中,这是与超精度有关的两个构造。但是我不知道有什么词可以解释为允许编译器将浮点值的精度从计算精度任意降低到类型精度。在对标准进行严格解释的情况下,这仅应以确定性方式在特定的位置(例如作业和演员表)进行。

最好的方法是阅读约瑟夫·迈尔斯(Joseph S. Myers)对以下相关部分的分析FLT_EVAL_METHOD

C99允许遵循某些规则以超范围和精度进行评估。这些在5.2.4.2.2第8段中概述:

除赋值和强制类型转换(删除所有额外的范围和精度)外,带有浮动操作数的运算值和需要进行常规算术转换的值以及带有浮点常量的值均以其范围和精度可能大于运算符要求的格式求值。类型。评估格式的使用以FLT_EVAL_METHOD的实现定义的值为特征:

约瑟夫·迈尔斯(Joseph S. Myers)在职位发布之前先描述了海湾合作委员会的情况。这种情况与您的编译器(以及其他众多编译器)一样糟糕:

使用x87浮点数时,GCC将FLT_EVAL_METHOD定义为2。但是,它的实现不符合FLT_EVAL_METHOD == 2的C99要求,因为它是由后端实现的,假装处理器支持SFmode和DFmode上的操作:

  • 有时,取决于优化,某个值可能会以SFmode或DFmode的形式溢出到内存中,因此,在C99指定丢失它的地方以及其他地方,意外地会丢失多余的精度。
  • 分配通常不会损失过多的精度,尽管-ffloat-store可能会增加它的准确性

C ++标准继承math.h自C99的定义,并且math.h是定义的标头FLT_EVAL_METHOD出于这个原因,您可能希望C ++编译器会效仿,但他们似乎并没有认真考虑这一问题。即使G ++仍不支持-fexcess-precision=standard,尽管它使用与GCC相同的后端(自Joseph S. Myers的帖子及其随附的补丁以来支持该选项)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

C语言中“ double”运算和优化

来自分类Dev

优化C语言中的按位运算

来自分类Dev

C语言中的循环优化

来自分类Dev

C语言中的位运算

来自分类Dev

优化C语言中的位解码操作

来自分类Dev

C语言中数组的算术运算

来自分类Dev

C语言中模运算的替代方法

来自分类Dev

了解C语言中的运算符

来自分类Dev

C语言中的double / float转换

来自分类Dev

返回值优化和C语言中的复制省略

来自分类Dev

C语言中的按位运算和掩码

来自分类Dev

C语言中的Putchar和Getchar

来自分类Dev

C语言中的方法和结构

来自分类Dev

C语言中的random(int)和randomize()

来自分类Dev

C语言中的叉子和指针

来自分类Dev

C语言中的变量声明和范围

来自分类Dev

C语言中的random(int)和randomize()

来自分类Dev

了解C语言中的指针和堆栈

来自分类Dev

C语言中的open()和write()函数

来自分类Dev

C语言中的数组和指针

来自分类Dev

malloc和C语言中的free

来自分类Dev

了解C语言中的getchar()和putchar()

来自分类Dev

C语言中“ *”,“ *”和“ *”指针之间的区别

来自分类Dev

C语言中的函数和指针

来自分类Dev

C语言中的LOCAL和GLOBAL函数

来自分类Dev

C语言中的textcolor()和textbackground()

来自分类Dev

C语言中宏和函数的区别

来自分类Dev

C语言中的random和Java语言中的random有什么区别吗?

来自分类Dev

C语言中的random和Java语言中的random之间有什么区别吗?