Taylor series expansion as constexpr
Asked Answered
S

1

4

I'm trying to build a simple sine function using taylor series expansion that can be evaluated at compile time using C++14 constexpr. My code is compiling, but the compiler doesn't generate a constant.

sine is defined as follows:

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;
}

I can provide code for power and factorial if needed. They are trivial and also constexpr.

I'm calling sine from within a loop like this:

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

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.

My compiler is clang from Xcode 7.2 compiling with -O3.

Spikenard answered 11/12, 2015 at 4:52 Comment(0)
C
7

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.

For a constexpr function to be evaluated at compile time the following must apply:

  • All of its input arguments must be constant expressions.
  • Its result must be used in a constant expression.

The assignment in the test's for loop is not a constant expression. Consequently, sine cannot be evaluated at compile time.

What you really want, is to statically initialize the elements of an array using sine(). Using a std::array and some helper machinery this is possible to do it as shown below:

#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

Cirro answered 11/12, 2015 at 16:46 Comment(4)
Isn't the issue that the argument to sine, i * M_PI / N, isn't a constexpr?Tilefish
@Tilefish "All of its input arguments must be constant expressions." is not clear enough?Cirro
I was referring to "The assignment in the test's for loop is not a constant expression". Is the assignment itself a problem here?Tilefish
@101010 Sorry to bother you. I recognize the Taylor series in the code you've posted but not the r value. How does the r value help for the calculation of sine values? Was also wondering: is the sine code perhaps missing the use of sgn in the computation of res?Churn

© 2022 - 2024 — McMap. All rights reserved.