我想编写一个简单的多项式类,它可以获取系数数组并在编译时将其扩展为函数,因此我不需要在运行时循环遍历系数。我想做这样的事情:
template <PARAM_TYPE, PARAMS>
class P {
public:
PARAM_TYPE eval(PARAM_TYPE p){
//Does PARAMS[0] * pow(p, PARAMS.length() -1) + ... + PARAMS[N-1]
}
}
示例电话
P<double,{2,4,3}> quadratic;
quadratic.eval(5); //returns 73
我不想做循环,因为这需要时间。理想情况下,我希望能够在编译时形成上面的表达式。这可能吗?感谢
答案 0 :(得分:1)
这是做你想做的事的一个例子。根据我注意到的用法以及你使用的编译器,编译器对于是否将所有代码优化为常量非常挑剔。
#include <type_traits>
template<class T, unsigned Exponent>
inline constexpr typename std::enable_if<Exponent == 0, T>::type
pow2(const T base)
{
return 1;
}
template<class T, unsigned Exponent>
inline constexpr typename std::enable_if<Exponent % 2 != 0, T>::type
pow2(const T base)
{
return base * pow2<T, (Exponent-1)/2>(base) * pow2<T, (Exponent-1)/2>(base);
}
template<class T, unsigned Exponent>
inline constexpr typename std::enable_if<Exponent != 0 && Exponent % 2 == 0, T>::type
pow2(const T base)
{
return pow2<T, Exponent / 2>(base) * pow2<T, Exponent / 2>(base);
}
template<typename ParamType>
inline constexpr ParamType polynomial(const ParamType&, const ParamType& c0)
{
return c0;
}
template<typename ParamType, typename Coeff0, typename ...Coeffs>
inline constexpr ParamType polynomial(const ParamType& x, const Coeff0& c0, const Coeffs& ...cs)
{
return (static_cast<ParamType>(c0) * pow2<ParamType, sizeof...(cs)>(x)) + polynomial(x, static_cast<ParamType>(cs)...);
}
unsigned run(unsigned x)
{
return polynomial(x, 2, 4, 3);
}
double run(double x)
{
return polynomial(x, 2, 4, 3);
}
unsigned const_unsigned()
{
static const unsigned value = polynomial(5, 2, 4, 3);
return value;
}
double const_double()
{
static const double value = polynomial(5, 2, 4, 3);
return value;
}
编辑:我已将代码更新为使用调整后的pow2<>()
版本,该版本会在编译时积极执行计算。这个版本在-O2
得到了很好的优化,实际上让我感到惊讶。您可以使用代码上方的按钮查看完整程序的生成程序集。如果所有参数都是常量,编译器将在编译时生成整个常量值。如果第一个参数依赖于运行时,它仍会为它生成非常严格的代码。
(感谢this question上的@dyp提供的灵感来源)
答案 1 :(得分:0)
为了评估多项式,一个好的算法是Horner(参见https://en.wikipedia.org/wiki/Horner%27s_method)。主要思想是递归计算多项式。设数为n的多项式P,系数为ai。很容易看出序列Pk = Pk-1 * x0 + an-k,P0 = a,P(x0)= Pn。
所以让我们使用constexpr函数实现这个算法:
template<class T>
constexpr double horner(double x, T an) { return an; }
template<class... T, class U = T>
constexpr double horner(double x, U an, T... a) { return horner(x, a...) * x + an; }
std::cout << horner(5., 1, 2, 1) << std::endl;
//test if the invocation of the constexpr function takes the constant expression branch
std::cout << noexcept(horner(5., 1, 2, 1)) << std::endl;
如您所见,使用递归公式实现使用constexpr函数对多项式进行求值非常容易。