More fun with constexpr

Continuing from my earlier post, here’s another look at how we can abuse the compiler and C++11’s compile-time abilities. This one might even be useful!

The Problem

Trigonometry functions are common in a lot of programming. On modern hardware, they’re usually highly optimized, too. But C++ developers are taught to use the compiler as much as possible. That’s why we have templates and `constexpr`, after all. So, can we do trig calculations this way?

The answer, of course, is “yes”. (If it wasn’t, then this post wouldn’t exist.) We’ll start with the cosine function, since it’s probably the easiest to implement. One way to define the cosine function is as an infinite series that I don’t really want to try to recreate in math notation here. As it turns out, that definition is fairly easily convertible to a recursive function that can be evaluated at compile-time.

The Code

``````template <int Iterations = 10>
constexpr double cosine (double theta)
{
return (power(-1, Iterations) * power(theta, 2 * Iterations)) /
static_cast<double>(factorial(2ull * Iterations))
+ cosine<Iterations-1>(theta);
}

template <>
constexpr double cosine<0> (double theta)
{
return 1.0;
}
``````

Since the Taylor series is an infinite one, we obviously can’t run the whole thing. So we approximate. I’ve decided on a default of 10 iterations, but the function, you should note, actually calculates the series “in reverse”. The base (i.e., last) case, when `Iterations == 0`, is the first term in the series expansion. It’s okay, though. The algorithm still works this way, although it might look a little weird.

Also, note that this cosine function uses two other `constexpr` functions. The first, `power()`, is our creation from last time, slightly renamed. The second is a typical factorial function. (It’s a good thing every single `constexpr` tutorial uses this one, otherwise I’d have to write it myself!) `factorial` takes an integer and returns another one, so I used an explicit cast to `double`. You could just change the definition of `factorial`, though, if you don’t mind the possible loss of precision. You’d probably want to template the whole thing on the type, too, instead of only using `double`s.

The other trig functions can, for the most part, work off this base. The sine series raises the angle to the power of `2n+1` instead of `2n` (and takes the factorial of `2n+1`, as well), but it’s otherwise the same. Tangent, as you know, is sine divided by cosine. The reciprocals (secant, cosecant, cotangent) are trivial. You’d also need conversions between degrees and radians (this function uses radians), but that’s so easy you can do it in your sleep.

In a real application, you might want to combine this with lookup tables or something, especially because of rounding errors and the limited precision of floating-point numbers.

Now What?

If I can find any more `constexpr` contortions, I’ll definitely be making more posts. There’s still so much of the standard math library left, you know.