泰勒级数展开为 constexpr

Taylor series expansion as constexpr

我正在尝试使用泰勒级数展开构建一个简单的 sine 函数,该函数可以在编译时使用 C++14 constexpr 进行计算。我的代码正在编译,但编译器没有生成常量。

sine定义如下:

template <int P, typename T = double> constexpr T sine(T x) {
    T result = x;

    for (int i = 1; i < P; ++i)
        result += power<T>(-1, i) * power<T>(x, 1 + 2 * i) / factorial<T>(1 + 2 * i);

    return result;
}

如果需要,我可以提供 powerfactorial 的代码。它们是微不足道的,也是 constexpr.

我在这样的循环中调用 sine

template <int N> void test(double *out) {
    for (int i = 0; i < N; ++i) {
        out[i] = sine<20, double>(i * M_PI / N);
    }
}

我原以为编译器可以为 sine 生成一组结果并将它们放入 out 而无需实际计算泰勒级数。相反,生成的代码执行 sine 就像它是任何其他非 constexpr 函数一样。

我的编译器是 Xcode 7.2 的 clang,使用 -O3.

编译

I was expecting that the compiler can generate a set of results for sine and put them into out without actually needing to compute the taylor series. Instead the generated code executes sine as if it was any other non-constexpr function.

对于要在编译时计算的 constexpr 函数,必须应用以下条件:

  • 它的所有输入参数都必须是常量表达式。
  • 其结果必须在常量表达式中使用。

test 的 for 循环中的赋值不是常量表达式。因此,sine 无法在编译时求值。

您真正想要的是使用 sine() 静态初始化数组的元素。使用 std::array 和一些辅助机制可以做到这一点,如下所示:

#define r 0.01745329251

constexpr double factorial(int n) {
  double res = 1.0;

  for(int i(2); i <= n; ++i) res *= i;

  return res;
}

template<typename T>
constexpr T power(T &&base, int const n) {

  if(!n) return 0.0;

  T res = base;

  for(int i(1); i < n; ++i) res *= base;

  return res;
}

template <typename T, int N = 5> 
constexpr T sine(T &&x) {
  T res = x * r;

  for (int i(3), sgn(-1); i <= N; i += 2, sgn = -sgn) {
    res += power(x * r, i) / factorial(i);
  }

  return res;
}

template <class T, std::size_t N, std::size_t... Is>
constexpr std::array<T, N> sine_array_impl(std::index_sequence<Is...>) {
  return {{sine(T{Is})...}}; 
}

template <class T, std::size_t N>
constexpr std::array<T, N> sine_array() {
  return sine_array_impl<T, N>(std::make_index_sequence<N>{});
}

Live Demo