Fortran(专为科学计算而设计)具有内置的幂运算符,就我所知,Fortran编译器通常会像您所描述的那样优化整数幂的求解。然而,C/C++不幸地没有幂运算符,只有库函数
pow()
。这并不妨碍聪明的编译器特别处理
pow
,并以更快的方式计算特定情况下的幂,但似乎它们并不经常这样做...
几年前,我试图使计算整数幂更加方便和优化,并提出了以下解决方法。虽然它是C++,而不是C,但仍然取决于编译器在如何优化/内联事物方面有一定的智能。无论如何,希望您在实践中能够发现它有用:
template<unsigned N> struct power_impl;
template<unsigned N> struct power_impl {
template<typename T>
static T calc(const T &x) {
if (N%2 == 0)
return power_impl<N/2>::calc(x*x);
else if (N%3 == 0)
return power_impl<N/3>::calc(x*x*x);
return power_impl<N-1>::calc(x)*x;
}
};
template<> struct power_impl<0> {
template<typename T>
static T calc(const T &) { return 1; }
};
template<unsigned N, typename T>
inline T power(const T &x) {
return power_impl<N>::calc(x);
}
解释一下:这并不是找到计算幂的最优方法,但是由于找到最优解是NP完全问题,而且这只适用于小指数(与使用pow
相比),所以没有必要过于关注细节。
然后只需使用power<6>(a)
即可。
这使得输入幂变得容易(无需用括号拼写6个a
),并且让您在需要精度依赖的情况下(例如补偿求和,这是一个需要操作顺序的示例)可以进行这种优化,而无需使用-ffast-math
。
您可能也可以忘记这是C++,并在C程序中使用它(如果它能够在C++编译器中编译通过)。
希望这对您有用。
编辑:
这是我从编译器得到的结果:
对于a*a*a*a*a*a
,
movapd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
针对表达式(a*a*a)*(a*a*a)
,
movapd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm0, %xmm0
对于power<6>(a)
,
mulsd %xmm0, %xmm0
movapd %xmm0, %xmm1
mulsd %xmm0, %xmm1
mulsd %xmm0, %xmm1
(a*a)*(a*a)*(a*a)
,同样的乘法次数,但可能更精确。 - Rok Kralj