std::abs(std::complex)过于缓慢。

4

为什么在一个大的复杂数组上运行 std::abs 的速度比使用 sqrtnorm 慢约8倍?

#include <ctime>
#include <cmath>
#include <vector>
#include <complex>
#include <iostream>
using namespace std;

int main()
{
    typedef complex<double> compd;

    vector<compd> arr(2e7);
    for (compd& c : arr)
    {
        c.real(rand());
        c.imag(rand());
    }

    double sm = 0;
    clock_t tm = clock();
    for (const compd& c : arr)
    {
        sm += abs(c);
    }
    cout << sm << ' ' << clock() - tm << endl; // 5.01554e+011 - 1640 ms

    sm = 0;
    tm = clock();
    for (const compd& c : arr)
    {
        sm += sqrt(norm(c));
    }
    cout << sm << ' ' << clock() - tm << endl; // 5.01554e+011 - 154

    sm = 0;
    tm = clock();
    for (const compd& c : arr)
    {
        sm += hypot(c.real(), c.imag());
    }
    cout << sm << ' ' << clock() - tm << endl; // 5.01554e+011 - 221
}

4
在任何关于性能的问题中,请包括您如何生成输入数据的详细信息,您进行了多少次实验,您如何进行计时,每个实验的时间等细节。 - hnefatl
1
Try -ffast-math? - Marc Glisse
我使用了其他编译器进行测试,得到了不同的结果。示例代码已添加。 - ashkan_d13
2
你的时间测量有一个错误,应该打印 clock()-tm 而不是 tm。同时,将你的测量结果添加到问题中也是明智的选择。 - geza
抱歉,我修复了代码并添加了结果。 - ashkan_d13
2个回答

9
我认为这两个函数在严格意义上不能视为相同的。来自cppreference中 std::abs(std :: complex)的说明是:
错误和特殊情况被处理得好像该函数是实现为std::hypot(std::real(z), std::imag(z)) 同时,cppreference中std::norm(std::complex)也是一样的:
由此函数计算的范数也称为场范数或绝对平方。复数的欧几里得范数由std::abs提供,但计算成本更高。在某些情况下,它可以被std::norm替换,例如如果 abs(z1)>abs(z2),则norm(z1)> norm(z2)
简而言之,有时每个函数都会产生不同的结果。其中一些可能在std::hypot中找到。笔记还提到如下内容: std::hypot(x, y)等价于std::abs(std::complex<double>(x,y)) 总体而言,结果的准确性可能会不同(由于通常的浮点混乱),这些函数似乎是为尽可能准确而设计的。

5
主要原因是 abs 在中间计算过程中处理了下溢和上溢问题。
因此,如果 norm 下/上溢,你的公式会返回错误/不准确的结果,而 abs 将返回正确的结果(例如,如果你的输入数字在 10200 范围内,则结果也应该约为 10200。但是你的公式将给出 inf 或浮点异常,因为中间的 norm 约为 10400,超出了范围。请注意,我这里假设使用 IEEE-754 64位浮点数)。
另一个原因是,abs 可能提供稍微精确一些的结果。
如果你不需要处理这些情况,因为你的输入数字“行为良好”(并且不需要可能更精确的结果),请随意使用你的公式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接