我可以保证C ++编译器不会重新排序我的计算吗?

时间:2011-10-05 02:29:52

标签: c++ optimization floating-point ieee-754

我目前正在阅读优秀的Library for Double-Double and Quad-Double Arithmetic论文,在前几行我注意到他们通过以下方式执行总结:

std::pair<double, double> TwoSum(double a, double b)
{
    double s = a + b;
    double v = s - a;
    double e = (a - (s - v)) + (b - v);
    return std::make_pair(s, e);
}

错误的计算e依赖于计算遵循该操作顺序的事实,因为IEEE-754浮点数学的非关联属性。

如果我在现代优化C ++编译器(例如MSVC或gcc)中编译它,我是否可以确保编译器不会优化计算方式?

其次,这在C ++标准中是否可以保证?

10 个答案:

答案 0 :(得分:6)

您可能希望查看g ++手册页:http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#Optimize-Options

特别是-fassociative-math,-ffast-math和-ffloat-store

根据g ++手册,除非您特别要求,否则不会重新排序您的表达式。

答案 1 :(得分:5)

这是一个非常有效的问题,因为英特尔的C ++编译器(使用非常广泛)默认执行可以改变结果的优化。

请参阅http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/copts/common_options/option_fp_model.htm#option_fp_model

答案 2 :(得分:4)

是的,这是安全的(至少在这种情况下)。你只在那里使用两个“运算符”,主表达式(something)和二进制something +/- something(添加剂)。

部分1.9 Program execution(C ++ 0x N3092)声明:

  

操作符可以按照通常的数学规则重新分组,只有操作符真正是关联的或可交换的。

就分组而言,5.1 Primary expressions州:

  

带括号的表达式是一个主表达式,其类型和值与所附表达式的类型和值相同。 ...除非另有说明,否则带括号的表达式可以与可以使用封闭表达式的上下文完全相同,并且具有相同的含义。

我认为在引用中使用“相同”一词需要一致的实现,以保证它将以指定的顺序执行,除非另一个订单可以给出 exact 相同的结果。

对于添加和减去,第5.7 Additive operators部分包含:

  

加法运算符+和 - 从左到右分组。

因此标准规定了结果。如果编译器可以确定可以通过不同的操作顺序获得相同的结果,那么它可以重新排列它们。但无论这种情况发生与否,你都无法辨别出差异。

答案 3 :(得分:2)

如果任何编译器错误地假设算术运算符与默认优化选项相关联,我会感到非常惊讶。

要注意FP寄存器的扩展精度

请参阅有关如何确保FP值没有扩展精度的编译器文档。

答案 4 :(得分:0)

通常,您应该能够 - 优化器应该了解实际操作的属性。

那就是说,我要测试我正在使用的编译器。

答案 5 :(得分:0)

是。编译器不会像这样更改块内的计算顺序。

答案 6 :(得分:0)

在处理器上的编译器优化和乱序执行之间,几乎可以保证事情不会像您订购的那样完全发生。

但是,也保证不会改变结果。 C ++遵循标准的操作顺序,所有优化都保留了这种行为。

底线:不要担心。编写C ++代码以在数学上正确并信任编译器。如果出现任何问题,问题几乎肯定不是编译器。

答案 7 :(得分:0)

如果你真的需要,我想你可以制作一个noinline函数no_reorder(float x){return x;然后使用它而不是括号。显然,它不是一个特别有效的解决方案。

答案 8 :(得分:-1)

根据其他答案,您应该能够依赖编译器做正确的事情 - 大多数编译器允许您编译和检查汇编程序(使用-S表示gcc) - 您可能希望这样做确定你得到了你期望的操作顺序。

不同的优化级别(在gcc中,-O _O2等)允许重新排列代码(但是这样的顺序代码不太可能受到影响) - 但我建议你应该将代码的特定部分隔离到一个单独的文件,以便您可以控制优化级别,仅用于计算。

答案 9 :(得分:-1)

简短的回答是:编译器可能会改变计算的顺序,但它永远不会改变程序的行为(除非你的代码使用带有未定义行为的表达式:http://blog.regehr.org/archives/213

但是,您仍然可以通过停用所有编译器优化来影响此行为(使用gcc选项“-O0”)。如果您仍然需要编译器来优化代码的其余部分,您可以将此函数放在单独的“.c”中,您可以使用“-O0”进行编译。 此外,您可以使用一些黑客。例如,如果您将代码与extern函数调用交错,编译器可能会认为重新排序代码是不安全的,因为该函数可能具有未知的副作用。调用“printf”来打印中间结果的值将导致类似的行为。

无论如何,除非你有任何理由(例如调试),否则你通常不想关心它,你应该相信编译器。