c++ - g++-带有-O选项的浮点行为不是严格的C++11标准保形?

标签 c++ c++11 gcc floating-point g++

考虑

int main() {
   double d = 1.0e+308;
   std::cout << (d*d)/1.0e+308;
}

在 Linux CentOS 系统上使用 g++ 版本 4.8.5 和两个编译器选项 -std=c++11 和 -mfpmath=387 编译(更准确地说,Linux 3.10.0-693.21.1.el7.x86_64 with Intel至强 X5690 CPU)。正如预期的那样,这会输出值 1e+308,因为乘法 d*d 是以 80 位扩展精度计算的,因此不会发生溢出(FLT_EVAL_METHOD 使用这些编译器设置返回 2)。现在考虑:

int main() {
   double d = 1.0e+308;
   double e;
   e = d*d;
   e = e/1.0e+308;
   std::cout << e;
}

这输出 inf - 再次如预期 - 因为 d*d 被分配给双参数 e 并且根据我对 ISO/IEC 14882:2011 标准 Sec 的理解。 5、11,脚注 60,e 的值必须是(或至少必须表现为)真正的 double 值,即在随后使用时必须是 inf。然而,这就是重点,当我将 -O1 添加到编译器选项时,程序的输出是 1e+308。这违反了 - 在我看来 - 分配(和强制转换)“执行其特定转换”的要求 - 请参阅上面提到的 c++11 标准文档中的段落。当使用优化级别 O1(或更高)时,我是否误解了这里的内容或者 gcc 不符合标准(在这方面)?

最佳答案

这是提到的段落:

The values of the floating operands and the results of floating expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby.

还有脚注:

The cast and assignment operators must still perform their specific conversions as described in 5.4, 5.2.9 and 5.17.

我不认为 GCC 违反了这一点。 e 以更高的精度表示,这是标准允许的(此处没有转换,因此脚注不适用)。

如果您不喜欢这种行为,请使用 -ffloat-store 选项,它会消除这种多余的精度,并且您的程序会打印 inf(但此选项使你的程序变慢了)。


注意,这个规范有点奇怪。

考虑一下。这是与您的示例相同的示例,但使用的是 float :

#include <iostream>

int main() {
   float d = 2.0e+38f;
   float e;
   e = d*d;
   e = e/2e+38f;
   std::cout << e;
}

这会打印 2e+38,而不是 inf。现在,如果您将 d 的类型更改为 double:在 e = d*d 处有一个转换,脚注适用,并且您的程序应该打印 inf。 GCC 的行为符合标准,它确实打印了 inf(使用 gcc 5.4.1 和 gcc 8.1.0 测试)。

关于c++ - g++-带有-O选项的浮点行为不是严格的C++11标准保形?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51382062/

相关文章:

android - 在 OpenGL ES 2 中绘制文本的正确方法

c++ - cout一个类的函数

c++ - 现代 C++ 启动和终止 Linux 程序的方法

c++ - C++11 正则表达式中有 match_partial 吗?

string - 从cv::Mat-> std::string-> cv::Mat的OpenCV转换会导致意外的副作用

c - c 中的 x64 内联汇编以对齐指令

c++ - `-Wl,` 编译器标志前缀

c++ - 并发 C++11 - 可以使用哪些工具链?

c++ - C++11 的标准库会有前向声明头吗?

docker - Alpine 上哪个库包含 cdefs.h?