std::fabs(a * b) 和 std::fabs(a) * std::fabs(b) 之间的区别
Difference between std::fabs(a * b) and std::fabs(a) * std::fabs(b)
我正在研究一些数字代码,我正在查看编译器输出。有一个特别的案例让我感到奇怪:
在实数中,它持有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));
}
这是 godbolt 中令人困惑的编译器输出,其中包含 gcc-10.1(撰写本文时的当前稳定版本(和 -O3:https://godbolt.org/z/ZEFPgF
值得注意的是,即使使用 -Ofast(据我了解,它对允许的转换更为宽松(,也不会执行此优化。
正如@Scheff在评论中指出的那样,双精度和浮点数不是实数。但是我也看不出浮点类型的极端情况(例如将无穷大或 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
https://godbolt.org/z/tf3nZN
这与你关于"实点/浮点"数字的说法相矛盾。
总的来说,你不应该指望编译器为你快捷方式数学问题。话虽如此,一些优化是可能的。请提供文档或您看到优化的类似示例。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 使用一个考虑到std::map中键值的滚动或换行的键
- 如何从 std::atomic 中提取指针 T<T>?
- 为什么 std::unique 不调用 std::sort?
- std::fabs(a * b) 和 std::fabs(a) * std::fabs(b) 之间的区别
- 当应用于浮点值时,std::abs和std::fabs之间有什么区别吗