考虑
int main() {
double d = 1.0e+308;
std::cout << (d*d)/1.0e+308;
}
在Linux CentOS系统(更确切地说是使用Intel的Linux 3.10.0-693.21.1.el7.x86_64)上与g ++版本4.8.5以及两个编译器选项-std = c ++ 11和-mfpmath = 387一起编译。至强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;
}
由于d * d被分配给双精度参数e,并且根据我对ISO / IEC 14882:2011标准Sec的理解,这会再次输出inf-符合预期。在图5、11,脚注60中,e的值必须是(或至少必须表现为)真双精度值,即,随后使用时必须是inf。但是,这就是要点,当我在编译器选项中添加-O1时,程序的输出为1e + 308。在我看来,这违反了赋值(和强制转换)“执行其特定转换”的要求-请参阅上述c ++ 11-standard文档中的段落。当使用优化级别O1(或更高)时,我在这里误解了吗?还是在这方面gcc不符合标准?
答案 0 :(得分:1)
这是提到的段落:
浮动操作数的值和浮动表达式的结果可以更大的形式表示 精度和范围超出类型要求;类型不会因此改变。
和脚注:
强制转换和赋值运算符仍必须按照5.4、5.2.9和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测试)。