super pi を作ろう

円周率πを100万桁まで計算するプログラムを作ってみました。super pi もどきです。ソースコードと解説を載せます。

ソースコードは80行程度の非常に短いものですが、このような簡単な実装でも最近のPCなら100万桁の円周率が一分も掛らず計算できます。

ポイント

要点は、

  • 計算精度が問題となるので、GMP(任意精度計算ライブラリ)を使う
  • 計算アルゴリズムは、Square AGM *1と呼ばれるものを使う

です。

GMPについては、http://d.hatena.ne.jp/pyopyopyo/20090303 をご覧下さい。

ソースコード

  1. /**
  2. * pi.cpp
  3. *
  4. * written by pyopyopyo at gmail dot com
  5. */
  6. #include <math.h>
  7. #include <gmpxx.h>
  8. #include <iostream>
  9. // N桁の円周率を計算
  10. #define N 100*100*100
  11. using namespace std;
  12. int
  13. main()
  14. {
  15. // N桁の数値計算に必要な精度を prec(bits) とすると
  16. //
  17. // prec = log2(10^N)
  18. // = N * log2(10)
  19. // 計算誤差を考えるのがめんどうなので,とりあえず(N+5)桁で計算してみる
  20. int prec = (N+5)*log2(10);
  21. mpf_set_default_prec(prec);
  22. fprintf(stderr, "prec:= %d bits\n", prec);
  23. const mpf_class two ("2.0");
  24. mpf_class a ("1.0");
  25. mpf_class b (a/sqrt(mpf_class("2.0")));
  26. mpf_class t (a);
  27. mpf_class x (a);
  28. mpf_div_ui(t.get_mpf_t(), t.get_mpf_t(), 4);
  29. mpf_class tmp(a);
  30. const int P = ceil(log2(N));
  31. for (int k=1; k<=P; ++k) {
  32. tmp = a;
  33. a = a + b;
  34. mpf_div_ui(a.get_mpf_t(), a.get_mpf_t(), 2);
  35. b = sqrt( tmp*b );
  36. tmp -= a;
  37. mpf_pow_ui(tmp.get_mpf_t(), tmp.get_mpf_t(), 2);
  38. t -= x * tmp;
  39. x *= two;
  40. }
  41. tmp = a + b;
  42. mpf_pow_ui(tmp.get_mpf_t(), tmp.get_mpf_t(), 2);
  43. tmp /= t;
  44. mpf_div_ui(tmp.get_mpf_t(), tmp.get_mpf_t(), 4);
  45. mp_exp_t expo;
  46. char *str = mpf_get_str(0, &expo, 10, N+1, tmp.get_mpf_t());
  47. for (int i=expo,count=1; i<N; i+=10, ++count) {
  48. char tmp[12]={0};
  49. strncpy(tmp, &str[i], 10);
  50. cout << tmp ;
  51. if (0 == (count%10))
  52. cout << endl;
  53. else
  54. cout <<" ";
  55. }
  56. return 0;
  57. }

コンパイル方法と、実行方法

$ gcc -lgmpxx pi.cpp -o pi
$ time ./pi

計算時間の目安としては

ぐらいになります。

なお出力は、以下のURLで公開されている100万桁の円周率πのデータの形式に合わせています。

ftp://pi.super-computing.org/pub/pi10m/pi10m.ascii.01of10

解説

  • 計算精度の指定
    int prec = (N+5)*log2(10);
    mpf_set_default_prec(prec);

gmpのデフォルトの計算精度を、(N+5)桁に設定しています。

log2(10^(N+5)) → (N+5)*log2(10) ということです。

なお、今回はN=100万なので、 precは約332万になります。つまり、GMPの変数は、一変数につき 400Kbyte 程度のメモリを消費することになります。


gmpc++インタフェースを利用します。ヘッダファイルは gmpxx.h です。

#include <gmpxx.h>

c++インタフェースを使うと、たとえば、gmpのcインタフェースで書いた以下のコードは

  1. mpf_t a, b, c;
  2. mpf_init(a);
  3. mpf_init(b);
  4. mpf_init(c);
  5. mpf_set_str(a, "1234", 10);
  6. mpf_set_str(b, "5678", 10);
  7. mpf_add(c, a, b);
  8. mpf_out_str(stdout, 10, 10, c);

c++インタフェースでは

  1. mpf_class a,b,c;
  2. a="1234";
  3. b="5678";
  4. c=a+b;
  5. cout << c;

と簡潔に記述できます。

さらに

 mpf_class a("1234"),b("5678"),c;
 c=a+b;
 cout << c;

とも書け、さらに圧縮して

 cout << mpf_class("1234")+mpf_class("5678");

と書くことも出来ます。


数値計算の常識
数値計算の常識
posted with amazlet at 09.03.09
伊理 正夫 藤野 和建
共立出版
売り上げランキング: 112092
おすすめ度の平均: 5.0
5 意外と忘れがちだったり、重要だけど知らなかったりするのが 常識なんです。
5 文字どおり「常識」にしたい内容
5 困る前に

ニューメリカルレシピ・イン・シー 日本語版―C言語による数値計算のレシピ
William H. Press William T. Vetterling Saul A. Teukolsky Brian P. Flannery
技術評論社
売り上げランキング: 9168
おすすめ度の平均: 4.0
4 教育的価値>実用的価値
5 最高です
4 第二版の翻訳がほしい
4 他の数値計算の参考書を買う前に
4 アルゴリズムの宝庫!

πの歴史 (ちくま学芸文庫)
ペートル ベックマン
筑摩書房
売り上げランキング: 152704