提问人:gbg 提问时间:10/21/2022 最后编辑:gbg 更新时间:10/21/2022 访问量:266
使用 -O0 和 -O2 编译的两个程序是否应该各自产生相同的浮点结果?
Should two programs compiled with -O0 and -O2 each produce identical floating point results?
问:
简短示例:
#include <iostream>
#include <string_view>
#include <iomanip>
#define PRINTVAR(x) printVar(#x, (x) )
void printVar( const std::string_view name, const float value )
{
std::cout
<< std::setw( 16 )
<< name
<< " = " << std::setw( 12 )
<< value << std::endl;
}
int main()
{
std::cout << std::hexfloat;
const float x = []() -> float
{
std::string str;
std::cin >> str; //to avoid
//trivial optimization
return strtof(str.c_str(), nullptr);
}();
const float a = 0x1.bac178p-5;
const float b = 0x1.bb7276p-5;
const float x_1 = (1 - x);
PRINTVAR( x );
PRINTVAR( x_1 );
PRINTVAR( a );
PRINTVAR( b );
PRINTVAR( a * x_1 + b * x );
return 0;
}
此代码在不同的平台/编译器/优化上产生不同的输出:
X = 0x1.bafb7cp-5 //this is float in the std::hexfloat notation
Y = 0x1.bafb7ep-5
输入值始终相同:0x1.4fab12p-2
编译器 | 优化 | x86_64 | aarch64 |
---|---|---|---|
GCC-12.2 的 | -O0 | X | X |
GCC-12.2 的 | -O2型 | X | Y |
叮当-14 | -O0 | X | Y |
叮当-14 | -O2型 | X | Y |
正如我们所看到的,Clang 在相同的架构中给出了 -O0 和 -O2 之间的相同结果,但 GCC 没有。
问题是 - 我们是否应该期望在同一平台上使用 -O0 和 -O2 获得相同的结果?
答:
问题是 - 我们是否应该期望在同一平台上使用 -O0 和 -O2 获得相同的结果?
不,一般不行。
C++ 2020 草案 N4849 7.1 [expr.pre] 6 说:
浮点操作数的值和浮点表达式的结果可以以比类型所需的精度和范围更高的精度和范围表示;因此,类型不会更改。51
脚注 51 说:
强制转换和赋值运算符仍必须按照 7.6.1.3、7.6.3、7.6.1.8 中所述执行其特定转换 和 7.6.19。
这意味着在计算时,C++实现可以使用操作数的名义类型,或者可以使用任何具有更高精度和/或范围的“超集”格式。这可能是 OR 或未命名的格式。完成计算并将结果分配给变量(在您的示例中包括函数参数)后,必须将使用扩展精度计算的结果转换为类型中可表示的值。因此,您将始终看到一个结果,但它可能与完全使用该类型执行算术的结果不同。a * x_1 + b * x
float
double
long double
float
float
float
C++ 标准不要求 C++ 实现对它在所有实例中使用的精度做出相同的选择。即使确实如此,编译器的每个命令行开关组合也可能被视为不同的 C++ 实现(至少对于可能影响程序行为的开关)。因此,获得的 C++ 实现可以通体使用算术,而获得的 C++ 实现可以使用扩展精度。-O0
float
-O2
请注意,用于计算的扩展精度不仅可以通过使用更广泛类型的机器指令(例如对值而不是值进行操作的指令)获得,还可以通过诸如融合乘加等指令产生,该指令的计算方式就好像以无限精度计算 •+,然后四舍五入到标称类型。这样可以避免先计算生成结果,然后再添加到 中时会发生的舍入错误。double
float
a*b+c
a
b
c
a*b
float
c
评论
FLT_EVAL_METHOD
long double
STDC FP_CONTRACT
评论
-Ofast
Should -O0 and -O2 produce identical results?
However, as library functions may not be correctly rounded for all arguments, function results may differ depending on whether they are evaluated at compile time or at run time.