コンパイル時cos()

コンパイル時に計算してくれるcos()関数ってなんか面白そう?と思って作ってみました。
関数をテイラー展開してそれを演算してるだけです。
やー、lispと違って末尾再帰じゃなくても折りたたまれるのは楽でいいなぁ(笑)

template <typename ValueType, int exponent>
class power
{
	typedef ValueType real_type;

public:
	static real_type value(const real_type base)
	{
		return base * power<real_type, exponent - 1>::value(base);
	}
};

template <typename ValueType>
class power<ValueType, 0>
{
	typedef ValueType real_type;
public:
	static real_type value(const real_type base)
	{
		return static_cast<real_type>(1);
	}
};

template <int depth>
struct factorial
{
	enum
	{
		value = depth * factorial<depth - 1>::value
	};
};

template <>
struct factorial<1>
{
	enum
	{
		value = 1
	};
};

template <typename ValueType, int max_depth, int depth>
struct taylor_expansion_cosine_term
{
	typedef ValueType real_type;

	static real_type value(const real_type& v)
	{
		const int n = max_depth - depth + 1;
		return (power<real_type, n>::value(-1.0) *
				power<real_type, n*2>::value(v)) /
			static_cast<real_type>(factorial<n*2>::value) +
			taylor_expansion_cosine_term
			<real_type, max_depth, depth - 1>::value(v);
	}
};

template <typename ValueType, int max_depth>
struct taylor_expansion_cosine_term<ValueType, max_depth, 1>
{
	typedef ValueType real_type;

	static real_type value(const real_type& /*v*/)
	{
		return static_cast<real_type>(0);
	}
};

template <typename ValueType, int calc_depth = 7>
class cosine
{
public:
	typedef ValueType real_type;
	
	static real_type value(real_type arg)
	{
		return static_cast<real_type>(1) + 
			taylor_expansion_cosine_term<real_type,
			calc_depth, calc_depth>::value(arg);
	}
};

inline double cos(const double angle)
{
	return cosine<double>::value(angle);
}


#include <iostream>
#include <iomanip>

const double pi = 3.14159265358979323846264338327;

void list_cos()
{
	for (double angle = -pi; angle <= pi; angle += 0.0001)
		std::cout << "angle: " << std::setw(11) << angle <<
			" value: " << std::setw(11) << cos(angle) << std::endl;
}

void static_compute_cos()
{
	std::cout << std::setw(11) << cos(pi/2.0) << std::endl;
}

int main()
{
	list_cos();
	std::cout << std::endl;
	static_compute_cos();

	return 0;
}
004012f0 <static_compute_cos()>:
  4012f0:	55                   	push   %ebp
  4012f1:	89 e5                	mov    %esp,%ebp
  4012f3:	83 ec 18             	sub    $0x18,%esp
  4012f6:	dd 05 08 00 44 00    	fldl   0x440008
  4012fc:	a1 c0 33 44 00       	mov    0x4433c0,%eax
  401301:	8b 40 f4             	mov    0xfffffff4(%eax),%eax
  401304:	05 c0 33 44 00       	add    $0x4433c0,%eax
  401309:	c7 40 08 0b 00 00 00 	movl   $0xb,0x8(%eax)
  401310:	dd 5c 24 04          	fstpl  0x4(%esp)
  401314:	c7 04 24 c0 33 44 00 	movl   $0x4433c0,(%esp)
  40131b:	e8 70 97 02 00       	call   42aa90 <std::basic_ostream<char, std::char_traits<char> >::operator<<(double)>

4012f6: dd 05 08 00 44 00 fldl 0x440008
401310: dd 5c 24 04 fstpl 0x4(%esp)

 浮動小数点スタックにロードしてからスタックに積むという多少余計なことはされていますが定数がそのまま使われています。
 対してlist_cos()では・・・

00401330 <list_cos()>:
  401330:	55                   	push   %ebp
  401331:	89 e5                	mov    %esp,%ebp
  401333:	83 ec 28             	sub    $0x28,%esp
  401336:	dd 05 00 00 44 00    	fldl   0x440000
  40133c:	dd 5d f8             	fstpl  0xfffffff8(%ebp)
  40133f:	80 75 ff 80          	xorb   $0x80,0xffffffff(%ebp)
  401343:	dd 05 28 00 44 00    	fldl   0x440028
  401349:	dd 45 f8             	fldl   0xfffffff8(%ebp)
  40134c:	e9 1c 01 00 00       	jmp    40146d <list_cos()+0x13d>
  401351:	dd 45 f8             	fldl   0xfffffff8(%ebp)
  401354:	b9 10 00 44 00       	mov    $0x440010,%ecx
  401359:	dd 05 60 00 44 00    	fldl   0x440060
  40135f:	d9 c9                	fxch   %st(1)
  401361:	89 4c 24 04          	mov    %ecx,0x4(%esp)
  401365:	d8 c8                	fmul   %st(0),%st
  401367:	c7 04 24 c0 33 44 00 	movl   $0x4433c0,(%esp)
  40136e:	dc c9                	fmul   %st,%st(1)
  401370:	dc 4d f8             	fmull  0xfffffff8(%ebp)
  401373:	d9 c9                	fxch   %st(1)
  401375:	d8 0d 30 00 44 00    	fmuls  0x440030
  40137b:	d9 c9                	fxch   %st(1)
  40137d:	dc 4d f8             	fmull  0xfffffff8(%ebp)
  401380:	d9 c9                	fxch   %st(1)
  401382:	dd 5d f0             	fstpl  0xfffffff0(%ebp)
  401385:	d9 c0                	fld    %st(0)
  401387:	dc 4d f8             	fmull  0xfffffff8(%ebp)
  40138a:	d9 c9                	fxch   %st(1)
  40138c:	d8 35 34 00 44 00    	fdivs  0x440034
  401392:	d9 c9                	fxch   %st(1)

 ... 以下、ずらずらと似たような演算が続いてます。

 cosine::value() が展開され、浮動少数点数の積和演算と除算がずらずらと並んでいます。

 というわけでtemplateベースでのコサイン関数を用意しとけば即値ならコンパイル時に値が計算され、そうでない場合は演算ルーチンが用意されるようです。

実験にはMinGW g++ 3.4.5とMinGW g++-dw2 4.2.1を使いましたが両方とも展開できてます。
ただしVisual C++ 2008 Express Editionだとコンパイルオプションいじったりしてみたんですが再帰templateが展開されてませんでした。
StandardやProfessionalに比べてコンパイラの最適化性能落としてるのか、それとも元々展開できないのかは不明です(2003Pro以降 gccで生活してるんで買ってないんです・・・)。