定义一个仅在指数为整数的情况下定义 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
函数。
如何在预处理过程中“写入”宏之前预先确定指数类型?
理想情况下,我希望仅在指数为整数时才应用此宏,但看来我的尝试并不恰当。
根据您的建议,我尝试了三个选项:
第一个选项:只需添加基数为 integer
或 double
:
的重载内联函数
// 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++ 代码后,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
函数。
如何在预处理过程中“写入”宏之前预先确定指数类型?
理想情况下,我希望仅在指数为整数时才应用此宏,但看来我的尝试并不恰当。
根据您的建议,我尝试了三个选项:
第一个选项:只需添加基数为 integer
或 double
:
// 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.