定义一个仅在指数为整数的情况下定义 pow 函数的宏

Define a macro which defines a pow function only in the case where the exponent is an integer

分析我的 C++ 代码后,pow 函数似乎被大量使用。

我的一些 pow 函数有一个整数指数和另一个非整数指数。我只对整数指数感兴趣。

为了提高性能,我正在寻找一种方法来定义这样的宏:

#define pow(x,n) ({\
    double product;\
    if (typeid(n).name() == "i") {\
    for(int i = 0; i < n-1; i++)\
        product *= x;}\
    else\
    product = pow(x,n);\
    product;\
})

但是我没有得到关于运行时的预期收益。我认为这是由于我的宏中的 else 部分,我在其中调用了经典的 pow 函数。

如何在预处理过程中“写入”宏之前预先确定指数类型?

理想情况下,我希望仅在指数为整数时才应用此宏,但看来我的尝试并不恰当。

根据您的建议,我尝试了三个选项:

第一个选项:只需添加基数为 integerdouble:

的重载内联函数
// First option
inline int pow(int x, int n){
    // using simple mechanism for repeated multiplication
    int product = 1;
    for(int i = 0; i < n; i++){
        product *= x;
    }
    return product;
}

inline int pow(double x, int n){
    // using simple mechanism for repeated multiplication
    double product = 1;
    for(int i = 0; i < n; i++){
        product *= x;
    }
    return product;
}

结果:运行时间 = 1 分 08 秒

第二个选项:定义一​​个宏,如果指数 n 不是整数,则通过内联 my_pow 函数调用:

// Second option
inline double my_pow(double x, double n){
    return pow(x,n);
}

#define pow(x,n) ({\
    double product = 1;\
    if (typeid(n) == typeid(int)) {\
    for(int i = 0; i < n; i++)\
        product *= x;}\
    else product = my_pow(x,n);\
    product;\
})

结果:运行时间 = 51.86 秒

第三个选项:在回答中给出的建议 template<typename type_t>

template<typename type_t>
inline double pow(const double x, type_t n)
{
    // This is compile time checking of types.
    // Don't use the RTTI thing you are trying to do
    //if constexpr (is_floating_point_v<type_t>)
    if (typeid(n) != typeid(int))
    {
        return pow(x, n);
    }
    else
    {
        double value = 1;
        for (type_t i = 0; i < n; ++i) value *= x;
        return value;
    }
}

结果:运行时间 = 52.84 秒

所以最后,从这些第一个测试中,最好的选择是第二个,我使用一个宏结合一个函数调用 pow 函数的一般情况(整数和浮点指数) .

是否有更有效的解决方案或第二种方案最好?

对 macro pow 版本使用 c++ 标准 https://www.cplusplus.com/reference/ctgmath/。或者对 pow 函数使用 constexpr 语句而不是宏。

一旦您确定您的 pow() 被使用,特此提出让您的 pow() 功能更加完善的建议。

这个想法可能很难实现,但如果你经常使用高功率,它可能是值得的,让我给你举个例子:

你想计算pow(a,17).
使用您的系统,您需要 16 次乘法。

现在我们把17转成二进制,你得到10001,也就是说,经过一些计算,你可以把pow(a,17)写成:

square(square(square(square(a))))*a, where square(a) = a*a

这只剩下 5 次乘法运算,可能会提高性能。事实上,它从 O(n)O(log n)(其中 n 是指数)。

编辑

让我向您展示您可以做到这一点:假设您需要计算一个数的 25 次方 n。然后,首先,您需要知道需要正方形的次数,使用简单的公式:

a = round_down(log(25) / log(2)) = 4

因此,您需要所有正方形,从 0 到 4,并创建以下数组(sqr(n) 代表 n 的正方形):

[1, n, sqr(n), sqr(sqr(n)), sqr(sqr(sqr(n))), sqr(sqr(sqr(sqr(n))))] with meanings:
 0, 1, 2,      4,           8,                16

你需要最后一部分(16次方),剩下9,比8大
你需要 8,剩下 1,它小于 4。 你不需要 4.
你不需要 2.
你需要 1,剩下 0,循环在这里停止。

So: n^25 = n * sqr(sqr(sqr(n))) * sqr(sqr(sqr(sqr(n))))

(我承认,解释不是100%,但你明白了)

如果您只需要在浮点类型之间切换或不需要,您可以使用模板而不是宏。

#include <cassert>
#include <cmath>
#include <type_traits>

namespace my_math
{
    template<typename type_t>
    inline double pow(const double x, type_t n)
    {
        // this is compile time checking of types
        // don't use the rtti thing you are trying to do 
        if constexpr (std::is_floating_point_v<type_t>)
        {
            return std::pow(x, n);
        }
        else
        {
            double value = 1;
            for (type_t i = 0; i < n; ++i) value *= x;
            return value;
        }
    };

}

int main()
{
    assert(my_math::pow(2, 0) == 1);
    assert(my_math::pow(2.0, 1) == 2.0);
    assert(my_math::pow(3.0, 2.0) == 9.0);
    assert(my_math::pow(4.0f, 3.0f) == 64.0f);

   return 0;
}

与其制作手动调整的解决方案,不如尝试使用编译器优化:-O3 -ffast-math-O2 -ffast-math

看看这个答案。

What is more efficient? Using pow to square or just multiply it with itself?

If you are using double precision (double), std::pow(x, n) will be slower than the handcrafted equivalent unless you use -ffast-math, in which case, there is absolutely no overhead.

C++11 Performance tip: When to use std::pow?