Ika*_*ros 12 c++ x86 g++ compiler-optimization clang++
我正在处理一些数字代码,我正在查看编译器输出。一个特殊的案例让我觉得很奇怪:
在实数中,它成立abs(a) * abs(b) = abs(a * b)
。我希望在浮点数中也是如此。但是,优化既不是由 clang 也不是由 g++ 执行的,我想知道我是否遗漏了一些细微的差异。然而,两个编译器都意识到abs(abs(a) * abs(b)) = abs(a) * abs(b)
.
这是相关的一段代码:
#include<cmath>
double fabsprod1(double a, double b) {
return std::fabs(a*b);
}
double fabsprod2(double a, double b) {
return std::fabs(a) * std::fabs(b);
}
double fabsprod3(double a, double b) {
return std::fabs(std::fabs(a) * std::fabs(b));
}
Run Code Online (Sandbox Code Playgroud)
这是带有 gcc-10.1(撰写本文时的当前稳定版本)和 -O3 的 Godbolt 中令人困惑的编译器输出:https ://godbolt.org/z/ZEFPgF
值得注意的是,即使使用 -Ofast,据我所知,它对允许的转换更为宽松,也不会执行此优化。
正如@Scheff 在评论中指出的那样,double 和 float 不是实数。但我也没有看到带有浮点类型的极端情况,例如将 Infinity 或 NaN 作为参数,可能会产生不同的输出。
我相信我已经找到了一个反例。我将其作为单独的答案发布,因为我认为这与整数的情况完全不同。
在我考虑的情况下,我错过了可以更改浮点运算的舍入模式。有问题的是,当 GCC(我猜)在编译时优化“已知”数量时,它似乎忽略了这一点。考虑以下代码:
#include <iostream>
#include <cmath>
#include <cfenv>
double fabsprod1(double a, double b) {
return std::fabs(a*b);
}
double fabsprod2(double a, double b) {
return std::fabs(a) * std::fabs(b);
}
int main() {
std::fesetround(FE_DOWNWARD);
double a = 0.1;
double b = -3;
std::cout << std::hexfloat;
std::cout << "fabsprod1(" << a << "," << b << "): " << fabsprod1(a,b) << "\n";
std::cout << "fabsprod2(" << a << "," << b << "): " << fabsprod2(a,b) << "\n";
#ifdef CIN
std::cin >> b;
#endif
}
Run Code Online (Sandbox Code Playgroud)
输出有所不同,具体取决于我是否使用编译
g++ -DCIN -O1 -march=native main2.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
或者
g++ -O1 -march=native main2.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
值得注意的是,只需要 O1(我认为完全可靠的)就可以以一种对我来说似乎不合理的方式改变输出。
使用 -DCIN 时,输出为
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333333p-2
Run Code Online (Sandbox Code Playgroud)
没有-DCIN 输出是
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
Run Code Online (Sandbox Code Playgroud)
编辑:Peter Cordes(感谢您的评论)指出,这个令人惊讶的结果是由于我未能告诉 GCC 尊重舍入模式的变化。通过以下命令构建,达到预期效果:
g++ -O1 -frounding-math -march=native main2.cpp && ./a.out
Run Code Online (Sandbox Code Playgroud)
(在我的机器上也适用于 O2 和 O3)。
归档时间: |
|
查看次数: |
201 次 |
最近记录: |