我正在处理一些数字代码,我在看编译器的输出。有一种特殊的情况让我觉得很奇怪。
在实数中,它认为 abs(a) * abs(b) = abs(a * b)
. 我希望在浮点数中也能保持同样的效果,但是优化既不是用clang也不是用g++,我想知道我是否漏掉了一些微妙的区别。然而,优化既不是由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));
}
这是gcc-10.1(写这篇文章时的稳定版)和-O3在godbolt的编译器中的混乱输出: https:/godbolt.orgzZEFPgF
值得注意的是,即使使用-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
}
输出不同,取决于我是否用以下方式编译
g++ -DCIN -O1 -march=native main2.cpp && ./a.out
或
g++ -O1 -march=native main2.cpp && ./a.out
值得注意的是,只需要O1(我认为是完全可靠的)就可以改变输出,而这种方式在我看来是不合理的。
使用-DCIN时,输出是
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333333p-2
没有-DCIN,输出为
fabsprod1(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
fabsprod2(0x1.999999999999ap-4,-0x1.8p+1): 0x1.3333333333334p-2
编辑:Peter Cordes (感谢你的评论)指出,这个令人惊讶的结果是由于我没有告诉GCC尊重四舍五入模式的改变。通过使用下面的命令进行构建,实现了预期的结果。
g++ -O1 -frounding-math -march=native main2.cpp && ./a.out
(在我的机器上,O2和O3也能工作)。
与intregrals相似的问题是(int
) :
#include<cmath>
int fabsprod1(int a, int b) {
return std::abs(a*b);
}
int fabsprod2(int a, int b) {
return std::abs(a) * std::abs(b);
}
int fabsprod3(int a, int b) {
return std::abs(std::abs(a) * std::abs(b));
}
结果是(使用您的选项) -O3 -std=c++2a -march=cannonlake
):
fabsprod1(int, int):
mov eax, edi
imul eax, esi
cdq
xor eax, edx
sub eax, edx
ret
fabsprod2(int, int):
mov eax, edi
cdq
xor eax, edx
sub eax, edx
mov edx, esi
sar edx, 31
xor esi, edx
sub esi, edx
imul eax, esi
ret
fabsprod3(int, int):
mov eax, edi
cdq
xor eax, edx
sub eax, edx
mov edx, esi
sar edx, 31
xor esi, edx
sub esi, edx
imul eax, esi
ret
这与你说的 "实浮点 "数字相矛盾。
总的来说,你不应该指望编译器为你快捷的解决数学问题。也就是说,一些优化是可能的。请提供文档或类似的例子来说明你看到优化的地方。