I'm using C++ constexpr
to evaluate, at compile time, the largest term of the calculated Fibonacci sequence that this code can correctly evaluate. To do this, I use this code:
constexpr unsigned long long fibo(unsigned n) {
return (n < 2) ? 1 : fibo(n-1)+fibo(n-2);
}
constexpr unsigned max_fibo(unsigned n) {
return (fibo(n+1) >= fibo(n)) ? max_fibo(n+1) : n;
}
const unsigned MAX_FIBO_TERM = max_fibo(0);
This works just fine and quickly constructs the correct value for MAX_FIBO_TERM
(92 on my machine). However, using that fibo
function to actually calculate terms at runtime is extremely slow (it takes almost 90 seconds to calculate only the first 48 terms).
I can use an alternative function to calculate the terms which is much faster:
unsigned long long fiboiter(unsigned n, unsigned long long &prev,
unsigned long long &prevprev) {
return prev = (n < 2) ? prevprev = 1 :
(std::swap(prev, prevprev), prev + prevprev);
}
However, I can't seem to make this into a valid constexpr
function. (This is why it's written in that strange way.) When I run this version, it takes 0.005 seconds on my machine.
When I compile with g++ -O2 -std=c++11 fibo.cpp -o fibo
it only takes 0.302 seconds.
My question is: How can I use the same efficient function both at runtime and compile time?
I'm using g++ 4.8.3 on a 64-bit Linux machine, but what I would like is a cross-platform universal answer rather than simply one specific to this compiler and machine. And no, C++14 is not yet an option in this situation.
Also, my question is similar to, but not the same as this question because I'm asking about how to use the same runtime-efficent code at compile time and not why or how the compile-time code is made efficient.
Here's the full program.
fibo.cpp
#include <iomanip>
#include <iostream>
#include <locale>
constexpr unsigned long long fibo(unsigned n) {
return (n < 2) ? 1 : fibo(n-1)+fibo(n-2);
}
unsigned long long fiboiter(unsigned n, unsigned long long &prev,
unsigned long long &prevprev) {
return prev = (n < 2) ? prevprev = 1 :
(std::swap(prev, prevprev), prev + prevprev);
}
constexpr unsigned max_fibo(unsigned n) {
return (fibo(n+1) >= fibo(n)) ? max_fibo(n+1) : n;
}
const unsigned MAX_FIBO_TERM = max_fibo(0);
int main(int argc, char **)
{
// format numeric output according to user's preferred locale
std::cout.imbue(std::locale{""});
unsigned long long prev, prevprev;
if (argc > 1) {
for (unsigned i = 0; i <= MAX_FIBO_TERM; ++i)
std::cout << "fib(" << std::setw(2) << i << ") = "
<< std::setw(26) << fiboiter(i, prev, prevprev) << '\n';
} else {
for (unsigned i = 0; i <= MAX_FIBO_TERM; ++i)
std::cout << "fib(" << std::setw(2) << i << ") = "
<< std::setw(26) << fibo(i) << '\n';
}
}
Sample output
fib( 0) = 1
fib( 1) = 1
fib( 2) = 2
fib( 3) = 3
fib( 4) = 5
fib( 5) = 8
...
fib(89) = 2,880,067,194,370,816,120
fib(90) = 4,660,046,610,375,530,309
fib(91) = 7,540,113,804,746,346,429
fib(92) = 12,200,160,415,121,876,738