std :: pow对于不同的指数非常不同的行为

时间:2016-06-27 17:47:16

标签: c++ performance numerical-methods

我目前正在尝试优化一些代码,其中50%的时间用于std::pow()。我知道指数总是是一个正整数,并且基数在区间(0,1)中总是一个双精度型。为了好玩,我写了一个函数:

inline double int_pow(double base, int exponent)
{
    double out = 1.0;
    for(int i = 0; i < exponent; i++)
    {
        out *= base;
    }

    return out;
}

我正在编译:

> g++ fast-pow.cpp -O3 --std=c++11

我在(0,1)之间生成了1亿个双打,并比较了(1)std::pow(2)上面的自制int_pow函数和(3)直接乘法的时间。这是我的计时例程草图(这是一个非常快速的综合测试):

void time_me(int exp, size_t reps)
{
    volatile double foo = 0.0;
    double base = 0.0;

    size_t i;
    for (i = 0; i < reps; ++i)
    {
        base = ((double) rand() / (RAND_MAX)) + 1;
        foo = pow(base, exp);
        // foo = int_pow(base, exp);
        // foo = base * base * base;
    }

    // check that the loop made it to the end
    std::cout << foo << "  " << i <<  std::endl;
}

int main()
{
    std::clock_t start;

    start = std::clock();
    time_me(3, 1e8);
    std::cout << "Time: " << (std::clock() - start) / (double)(CLOCKS_PER_SEC / 1000) << std::endl;

    return 0;
}

以下是我对各种指数的观察时间:

  • 0: std::pow 0.71s,int_pow 0.77s
  • 2: std::pow 1.31s,int_pow 0.80秒,直接多人0.86秒
  • 3: std::pow 6.9s (!!)int_pow 0.84s,direct mult 0.76s
  • 5: 3:
  • 类似

我的问题

因此,我的问题是:

  1. 为什么std::pow的效果对于大于2的功率而言似乎会严重降低?
  2. 当基本或指数类型提前知道时,是否存在更快的幂函数?
  3. 有什么东西是我忽略的吗?对于已知整数指数的情况,我即将通过肠道std::pow,并且不愿错过一些完全无关紧要的事情。
  4. 谢谢!

2 个答案:

答案 0 :(得分:8)

std::pow()是一个通用函数,旨在接受任何一对浮点值。它执行昂贵的计算,应该被认为是一个缓慢的功能。然而,显然,很多人已经滥用它进行平方,因此在IBM Accurate Mathematical Library(由glibc使用)中pow()的实现针对该特定情况进行了优化:

sysdeps/ieee754/dbl-64/e_pow.c

double
__ieee754_pow (double x, double y)
{
  ...
  ...
  if (y == 1.0)
    return x;
  if (y == 2.0)
    return x * x;
  if (y == -1.0)
    return 1.0 / x;
  if (y == 0)
    return 1.0;

正如你所看到的,指数值0,1和-1也是专门处理的,但至少这些是数学上重要的特殊情况,而平方仅仅是一个统计上显着的情况,否则不值得特殊处理)。 编辑:指数值012-1是唯一允许std::pow(x,n)表达的值更快的算术运算,没有任何精度损失。有关详细信息,请参阅this answer。因此,2的指数值不仅仅是一个具有统计意义的案例。 结束编辑

如果您希望快速替换std::pow()指数的非负整数值,并且不关心轻微的精度损失,那么

  1. 对于指数的足够小的值,使用你的int_pow();
  2. 的实现
  3. 否则,请使用exponentiation by squaring approach
  4. 必须通过仔细的基准测试找到用于在第一种方法和第二种方法之间进行选择的指数的边界值。

答案 1 :(得分:1)

explode()