我正在处理一些数字代码,我正在查看编译器输出。一个特殊的案例让我觉得很奇怪:
在实数中,它成立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 作为参数,可能会产生不同的输出。
今天我想测试一下,Clang 如何转换两个函数的递归幂,并注意到即使使用已知指数,即使使用 constexpr 也不会优化递归。
#include <array>
constexpr unsigned int pow2_recursive(unsigned int exp) {
if(exp == 0) return 1;
return 2 * pow2_recursive(exp-1);
}
unsigned int pow2_5() {
return pow2_recursive(5);
}
Run Code Online (Sandbox Code Playgroud)
pow2_5 被编译为对 pow2_recursive 的调用。
pow2_5(): # @pow2_5()
mov edi, 5
jmp pow2_recursive(unsigned int) # TAILCALL
Run Code Online (Sandbox Code Playgroud)
但是,当我在需要在编译时知道结果的上下文中使用结果时,它将在编译时正确计算结果。
unsigned int pow2_5_arr() {
std::array<int, pow2_recursive(5)> a;
return a.size();
}
Run Code Online (Sandbox Code Playgroud)
被编译为
pow2_5_arr(): # @pow2_5_arr()
mov eax, 32
ret
Run Code Online (Sandbox Code Playgroud)
以下是 Godbolt 中完整示例的链接:https ://godbolt.org/z/fcKef1
那么,我在这里错过了什么吗?有什么可以在运行时改变结果的原因吗,pow2_5 不能像 pow2_5_arr 一样优化?