在“双"上进行操作C语言的优化 [英] Operations on "double" and optimization in C

查看:66
本文介绍了在“双"上进行操作C语言的优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近分析了用VS2005编译的一段旧代码,因为调试"(无优化)和发布"(/O2/Oi/Ot/Ot选项)编译中的数值行为不同.(简化的)代码如下:

  void f(双倍x1,双倍y1,双倍x2,双倍y2){双重a1,a2,d;a1 = atan2(y1,x1);a2 = atan2(y2,x2);d = a1-a2;if(d == 0.0){//注意:我知道==实际上是邪恶的"!printf("EQUAL!\ n");} 

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

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

那么,简而言之,C标准对这种情况有何看法?

解决方案

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

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

因此,简而言之,C标准对这种情况有何看法?

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

最好是阅读 Joseph S. Myers的分析 FLT_EVAL_METHOD 相关的零件中的a>:

C99允许在超出范围和精度的情况下进行评估某些规则.这些已在5.2.4.2.2第8段中概述:

除了分配和强制转换(删除所有额外的范围和精度),带有浮动操作数的运算值和值需要进行常规的算术转换和浮点运算常量被评估为一种格式,其范围和精度可能大于类型要求.使用评价格式的特点是实现的定义值FLT_EVAL_METHOD:

约瑟夫·迈尔斯(Joseph S. Myers)继续在其职位随附的补丁之前描述GCC的情况.这种情况与您的编译器(以及其他编译器)一样糟糕:

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

...

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

I have recently analyzed an old piece of code compiled with VS2005 because of a different numerical behaviour in "debug" (no optimizations) and "release" (/O2 /Oi /Ot options) compilations. The (reduced) code looks like:

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");
}

The function f is expected to print "EQUAL" if invoked with identical pairs of values (e.g. f(1,2,1,2)), but this doesn't always happen in "release". Indeed it happened that the compiler has optimized the code as if it were something like d = a1-atan2(y2,x2) and removed completely the assignment to the intermediate variable a2. Moreover, it has taken advantage of the fact that the second atan2()'s result is already on the FPU stack, so reloaded a1 on FPU and subtracted the values. The problem is that the FPU works at extended precision (80 bits) while a1 was "only" double (64 bits), so saving the first atan2()'s result in memory has actually lost precision. Eventually, d contains the "conversion error" between extended and double precision.

I know perfectly that identity (== operator) with float/double should be avoided. My question is not about how to check proximity between doubles. My question is about how "contractual" an assignment to a local variable should be considered. By my "naive" point of view, an assignment should force the compiler to convert a value to the precision represented by the variable's type (double, in my case). What if the variables were "float"? What if they were "int" (weird, but legal)?

So, in short, what does the C standard say about that cases?

解决方案

By my "naive" point of view, an assignment should force the compiler to convert a value to the precision represented by the variable's type (double, in my case).

Yes, this is what the C99 standard says. See below.

So, in short, what does the C standard say about that cases?

The C99 standard allows, in some circumstances, floating-point operations to be computed at a higher precision than that implied by the type: look for FLT_EVAL_METHOD and FP_CONTRACT in the standard, these are the two constructs related to excess precision. But I am not aware of any words that could be interpreted as meaning that the compiler is allowed to arbitrarily reduce the precision of a floating-point value from the computing precision to the type precision. This should, in a strict interpretation of the standard, only happen in specific spots, such as assignments and casts, in a deterministic fashion.

The best is to read Joseph S. Myers's analysis of the parts relevant to FLT_EVAL_METHOD:

C99 allows evaluation with excess range and precision following certain rules. These are outlined in 5.2.4.2.2 paragraph 8:

Except for assignment and cast (which remove all extra range and precision), the values of operations with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type. The use of evaluation formats is characterized by the implementation-defined value of FLT_EVAL_METHOD:

Joseph S. Myers goes on to describe the situation in GCC before the patch that accompanies his post. The situation was just as bad as it is in your compiler (and countless others):

GCC defines FLT_EVAL_METHOD to 2 when using x87 floating point. Its implementation, however, does not conform to the C99 requirements for FLT_EVAL_METHOD == 2, since it is implemented by the back end pretending that the processor supports operations on SFmode and DFmode:

  • Sometimes, depending on optimization, a value may be spilled to memory in SFmode or DFmode, so losing excess precision unpredictably and in places other than when C99 specifies that it is lost.
  • An assignment will not generally lose excess precision, although -ffloat-store may make it more likely that it does.

The C++ standard inherits the definition of math.h from C99, and math.h is the header that defines FLT_EVAL_METHOD. For this reason you might expect C++ compilers to follow suit, but they do not seem to be taking the issue as seriously. Even G++ still does not support -fexcess-precision=standard, although it uses the same back-end as GCC (which has supported this option since Joseph S. Myers' post and accompanying patch).

这篇关于在“双"上进行操作C语言的优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆