This piece of code is supposed to calculate an approximation to e (i.e. the mathematical constant ~ 2.71828183) at compile-time, using the following approach;
e1 = 2 / 1
e2 = (2 * 2 + 1) / (2 * 1) = 5 / 2 = 2.5
e3 = (3 * 5 + 1) / (3 * 2) = 16 / 6 ~ 2.67
e4 = (4 * 16 + 1) / (4 * 6) = 65 / 24 ~ 2.708
...
e(i) = (e(i-1).numer * i + 1) / (e(i-1).denom * i)
The computation is returned via the result
static member however, after 2 iterations it yields zero instead of the expected value. I've added a static member function f() to compute the same value and that doesn't exhibit the same problem.
#include <iostream>
#include <iomanip>
// Recursive case.
template<int Iters, int Num = 2, int Den = 1, int I = 2>
struct CalcE
{
static const double result;
static double f () {return CalcE<Iters, Num * I + 1, Den * I, I + 1>::f ();}
};
template<int Iters, int Num, int Den, int I>
const double CalcE<Iters, Num, Den, I>::result = CalcE<Iters, Num * I + 1, Den * I, I + 1>::result;
// Base case.
template<int Iters, int Num, int Den>
struct CalcE<Iters, Num, Den, Iters>
{
static const double result;
static double f () {return result;}
};
template<int Iters, int Num, int Den>
const double CalcE<Iters, Num, Den, Iters>::result = static_cast<double>(Num) / Den;
// Test it.
int main (int argc, char* argv[])
{
std::cout << std::setprecision (8);
std::cout << "e2 ~ " << CalcE<2>::result << std::endl;
std::cout << "e3 ~ " << CalcE<3>::result << std::endl;
std::cout << "e4 ~ " << CalcE<4>::result << std::endl;
std::cout << "e5 ~ " << CalcE<5>::result << std::endl;
std::cout << std::endl;
std::cout << "e2 ~ " << CalcE<2>::f () << std::endl;
std::cout << "e3 ~ " << CalcE<3>::f () << std::endl;
std::cout << "e4 ~ " << CalcE<4>::f () << std::endl;
std::cout << "e5 ~ " << CalcE<5>::f () << std::endl;
return 0;
}
I've tested this with VS 2008 and VS 2010, and get the same results in each case:
e2 ~ 2
e3 ~ 2.5
e4 ~ 0
e5 ~ 0
e2 ~ 2
e3 ~ 2.5
e4 ~ 2.6666667
e5 ~ 2.7083333
Why does result
not yield the expected values whereas f()
does?
According to Rotsor's comment below, this does work with GCC, so I guess the question is, am i relying on some type of undefined behaviour with regards to static initialisation order, or is this a bug with Visual Studio?