C++でπを扱う方法には、大きく分けて「πの値を取得して使う方法」と「アルゴリズムによってπを近似計算する方法」があります。
実務で円の面積や三角関数の計算にπを使いたいだけなら、自分でπを計算する必要はほとんどありません。
C++の標準機能や数学関数を使えば、十分な精度のπを簡単に利用できます。
一方で、数値計算やアルゴリズムの学習を目的とする場合は、ライプニッツ級数、ニラカンタ級数、マチンの公式、モンテカルロ法、ガウス=ルジャンドル法、チュドノフスキー法などを使って、πを自分で近似計算することもできます。
ただし、πの計算では「どの式を使うか」だけでなく、「どの数値型を使うか」も非常に重要です。
double には精度の限界があるため、どれだけ優れたアルゴリズムを使っても、標準的な浮動小数点型だけでは表現できる桁数に上限があります。
実務でπを使うなら標準機能を使う
C++20以降では標準のπ定数が使える
C++20以降では、標準ライブラリに数学定数が用意されています。
そのため、πを使いたいだけであれば、標準で提供されているπ定数を使うのが最も自然です。
この方法は、コードの意図が明確で、実装ミスも起こりにくいという利点があります。
円の面積、円周、角度変換、三角関数の計算など、一般的な用途であれば標準定数を使うだけで十分です。
ただし、この機能はC++20以降で利用できるものです。
古いC++規格でコンパイルしている場合は使えないため、コンパイラの設定に注意が必要です。
C++20以前では逆余弦関数からπを求める方法がある
C++20より前の環境では、逆余弦関数を使ってπを得る方法がよく使われます。
数学的に、πの余弦はマイナス1です。
そのため、マイナス1の逆余弦を求めることで、πの値を得ることができます。
この方法は古くから使われており、C++20が使えない環境では実用的です。
ただし、C++20以降であれば、標準のπ定数を使った方がコードの意味がわかりやすくなります。
πの表示桁数と計算精度の違い
表示桁数を増やしても精度が上がるわけではない
C++でπを扱うときに注意したいのが、表示桁数と計算精度は別物だという点です。
たとえば、出力時に多くの桁を表示するよう指定しても、内部で保持している値の精度が高くなるわけではありません。
double で計算している場合、内部で表現できる有効桁数には限界があります。
つまり、画面上にたくさんの桁が表示されていても、そのすべてが数学的に正しいπの桁とは限りません。
表示桁数を増やすことは、あくまで「見せる桁数」を増やすだけであり、「計算の正確さ」を増やすことではありません。
double の精度には限界がある
C++でよく使われる double 型は、多くの環境でおよそ15〜17桁程度の有効精度を持ちます。
そのため、πを double で扱う場合、正確に表現できるのはその範囲までです。
小数点以下を何十桁も表示したとしても、後半の桁は正確なπの値ではない可能性があります。
高精度なπを求めたい場合は、double ではなく、多倍長計算に対応した型やライブラリを使う必要があります。
long double は環境によって精度が変わる
double より高精度な型として long double があります。
ただし、long double の精度は環境によって異なります。
ある環境では double より高い精度を持ちますが、別の環境では double とほとんど同じ扱いになることもあります。
そのため、long double を使えば必ず高精度になるとは限りません。
確実に高精度な計算をしたい場合は、Boost.Multiprecisionなどの多倍長計算ライブラリを使う方が安全です。
ライプニッツ級数でπを計算する方法
ライプニッツ級数の考え方
ライプニッツ級数は、πを近似する有名な級数のひとつです。
奇数の逆数を、プラス、マイナス、プラス、マイナスと交互に足し引きしていくことで、πの4分の1に近づいていきます。
最後にその値を4倍することで、πを近似できます。
考え方が非常にシンプルなため、C++でπを自分で計算する入門として扱いやすい方法です。
ライプニッツ級数のメリット
ライプニッツ級数のメリットは、仕組みがわかりやすいことです。
複雑な数学関数を使わず、単純な足し算、引き算、割り算、繰り返し処理だけでπを近似できます。
そのため、C++のループ処理や浮動小数点計算の練習にも向いています。
ライプニッツ級数のデメリット
ライプニッツ級数の大きな欠点は、収束が非常に遅いことです。
多くの項を計算しても、πの正しい桁数はなかなか増えません。
たとえば、かなり大きな回数の計算を行っても、小数点以下の数桁程度しか安定しないことがあります。
そのため、ライプニッツ級数は高精度なπ計算には向いていません。
学習用の題材として使うのが適切です。
ニラカンタ級数でπを計算する方法
ニラカンタ級数の考え方
ニラカンタ級数は、3を出発点として、特定の分数を交互に足し引きしていくことでπに近づける方法です。
分母には連続する3つの整数の積が使われ、項を重ねるごとにπの近似値が改善されていきます。
ライプニッツ級数よりも収束が速いため、同じ学習用の級数でも、より実用的な近似に近づきやすい方法です。
ライプニッツ級数より精度を出しやすい
ニラカンタ級数は、ライプニッツ級数よりも少ない項数でπに近づきます。
そのため、級数による近似計算を学ぶ場合、ライプニッツ級数の次に試す方法として適しています。
両者を比較すると、収束速度の違いを実感しやすくなります。
項数の扱いに注意する
ニラカンタ級数を説明するときは、何項分を計算するのかを明確にすることが大切です。
単に繰り返し回数だけで説明すると、どの項をどこまで足しているのかがわかりにくくなる場合があります。
記事や教材で扱う場合は、「1項目ではどの数を使うのか」「2項目ではどの数を使うのか」といった形で説明すると、読み手に伝わりやすくなります。
モンテカルロ法でπを計算する方法
モンテカルロ法の考え方
モンテカルロ法は、乱数を使ってπを近似する方法です。
代表的な考え方として、正方形の中に円を描き、その正方形内にランダムに点を打つ方法があります。
打った点のうち、円の内側に入った点の割合を調べることで、πを近似できます。
半径1の円の面積はπであり、その円を含む一辺2の正方形の面積は4です。
そのため、ランダムに打った点が円の中に入る割合は、理論上はπを4で割った値に近づきます。
確率やシミュレーションの学習に向いている
モンテカルロ法は、確率や乱数、シミュレーションの考え方を学ぶのに適しています。
πを単純な数式で求めるのではなく、ランダムな試行を大量に行い、その結果から近似値を得るという点が特徴です。
そのため、数学的な級数とは違うアプローチでπを求めたい場合に面白い題材になります。
高精度計算には向いていない
モンテカルロ法は、実行するたびに結果が変わります。
乱数を使うため、同じ回数だけ点を打っても、毎回まったく同じ値になるとは限りません。
また、精度を上げるには非常に多くの試行回数が必要です。
そのため、モンテカルロ法は高精度なπ計算には向いていません。
正確なπを求めるというより、確率的な近似の仕組みを理解するための方法と考えるのがよいでしょう。
マチンの公式でπを計算する方法
マチンの公式の考え方
マチンの公式は、逆正接関数を使ってπを求める方法です。
πを、特定の逆正接関数の組み合わせとして表すことで、効率よく近似できます。
代表的な形では、1/5の逆正接と1/239の逆正接を組み合わせてπを求めます。
この方法は、ライプニッツ級数よりもはるかに速く収束します。
学習用としてバランスがよい
マチンの公式は、C++でπを自分で計算する学習題材として非常にバランスがよい方法です。
ライプニッツ級数は簡単ですが、収束が遅すぎます。
一方で、チュドノフスキー法のような超高精度向けの方法は、実装が複雑です。
その中間にあるマチンの公式は、比較的実装しやすく、それでいて高い精度を得やすいという特徴があります。
高精度化には多倍長計算が必要
マチンの公式は収束が速い方法ですが、double で計算する限り、得られる精度には限界があります。
より多くの桁を正確に求めたい場合は、Boost.Multiprecisionのような多倍長計算ライブラリと組み合わせる必要があります。
つまり、マチンの公式そのものは高精度計算に向いていますが、実際に高精度を得るには、それに対応した数値型が必要です。
ガウス=ルジャンドル法でπを計算する方法
ガウス=ルジャンドル法の考え方
ガウス=ルジャンドル法は、反復計算によってπを高速に近似する方法です。
算術平均と幾何平均を使いながら複数の変数を更新し、最終的にπの近似値を求めます。
この方法は収束が非常に速く、少ない反復回数で高い精度に到達できることが特徴です。
少ない反復で高精度に近づく
ガウス=ルジャンドル法は、ライプニッツ級数やニラカンタ級数と比べると、非常に効率的です。
数回の反復だけで、標準的な double の精度限界近くまで到達できます。
そのため、反復法による高速な近似計算を学ぶには適した方法です。
double ではすぐ精度上限に達する
ガウス=ルジャンドル法は収束が速いため、double で実装すると、すぐに型の精度上限に達します。
その後に反復回数を増やしても、正しい桁数が大きく増えるわけではありません。
これはアルゴリズムが悪いのではなく、使っている数値型の限界によるものです。
高精度な計算をしたい場合は、ガウス=ルジャンドル法も多倍長計算と組み合わせる必要があります。
チュドノフスキー法でπを計算する方法
チュドノフスキー法の考え方
チュドノフスキー法は、πの超高精度計算で知られている方法です。
非常に収束が速く、1項あたり多くの桁数を得られるため、πを大量の桁数まで計算する場面で使われることがあります。
一般的な学習用の近似式よりもはるかに強力ですが、その分、数式も実装も複雑です。
超高精度計算に向いている
チュドノフスキー法は、数十桁程度ではなく、数百桁、数千桁、さらにそれ以上のπを求めるような高精度計算に向いています。
ただし、その性能を活かすには、通常の double や long long では不十分です。
大きな整数や高精度な小数を扱える仕組みが必要になります。
実装難度は高い
チュドノフスキー法では、階乗や大きな累乗など、非常に大きな数が登場します。
そのため、通常の整数型ではすぐにオーバーフローします。
また、浮動小数点の精度にも注意が必要です。
本格的に実装するには、多倍長整数、多倍長浮動小数点、計算量を抑えるための工夫が必要になります。
初心者向けというより、発展的な数値計算のテーマとして扱うのが適しています。
Boost.Multiprecisionを使った高精度計算
double より多くの桁を扱える
C++で高精度なπを扱いたい場合、Boost.Multiprecisionがよく使われます。
Boost.Multiprecisionを使うと、標準の double よりも多くの桁を持つ数値型を利用できます。
50桁程度、100桁程度、さらにそれ以上の精度を持つ型を使うことで、通常の浮動小数点型では扱えない桁数の計算が可能になります。
固定された精度の型として理解する
Boost.Multiprecisionを説明するときは、「無限に正確な型」と誤解しないように注意が必要です。
たとえば、50桁程度の精度を持つ型、100桁程度の精度を持つ型というように、あらかじめ精度が決まった型を選んで使います。
必要な桁数が増えれば、それに応じてより高い精度の型を選ぶ必要があります。
多倍長計算だからといって、何もしなくても無制限に正確になるわけではありません。
高精度アルゴリズムと組み合わせると効果的
Boost.Multiprecisionは、マチンの公式、ガウス=ルジャンドル法、チュドノフスキー法などと組み合わせることで効果を発揮します。
高精度なアルゴリズムを使っても、数値型が double のままでは精度に限界があります。
逆に、多倍長型を使っても、収束の遅いアルゴリズムでは効率が悪くなります。
高精度なπを求めるには、「収束の速い計算方法」と「十分な精度を持つ数値型」の両方が必要です。
C++でπを計算するときの注意点
アルゴリズムの速さと型の精度は別問題
πの計算では、アルゴリズムの収束速度と、数値型の精度を分けて考える必要があります。
ライプニッツ級数のように収束が遅い方法では、何度計算してもなかなか正しい桁が増えません。
一方で、ガウス=ルジャンドル法のように収束が速い方法でも、double を使っている限り、一定以上の桁数は正確に表現できません。
つまり、高精度なπ計算には、効率のよいアルゴリズムだけでなく、それを受け止められる高精度な数値型が必要です。
表示される桁がすべて正しいとは限らない
C++では、出力設定によって多くの桁を表示できます。
しかし、表示された桁がすべて正しいとは限りません。
内部で保持している値に誤差があれば、その誤差も含めて表示されます。
特に double でπを何十桁も表示した場合、後半の桁は正確なπではない可能性が高いです。
高精度な値を表示したい場合は、計算段階から高精度な型を使う必要があります。
実行環境によって結果が変わる場合がある
C++の浮動小数点計算は、多くの場合は安定していますが、細かい部分では環境によって違いが出ることがあります。
特に long double の精度や、出力される桁の見え方は、コンパイラや処理系によって変わる場合があります。
また、モンテカルロ法のように乱数を使う方法では、実行するたびに結果が変わります。
再現性が必要な場合は、乱数の種を固定するなどの工夫が必要です。
目的別に見るπの計算方法
実務でπを使いたい場合
実務でπを使いたいだけなら、標準ライブラリのπ定数を使うのが最もおすすめです。
C++20以降なら標準のπ定数を使い、C++20より前なら逆余弦関数を使ってπを得る方法が現実的です。
この用途では、自分でライプニッツ級数やモンテカルロ法を実装する必要はありません。
むしろ、自作の近似計算は誤差や実装ミスの原因になるため、標準機能を使う方が安全です。
アルゴリズムの学習をしたい場合
π計算を通じてアルゴリズムを学びたい場合は、段階的に方法を試すのがおすすめです。
最初はライプニッツ級数で、級数による近似の基本を理解できます。
次にニラカンタ級数を試すと、収束速度の違いを体感できます。
その後、マチンの公式に進むと、より効率的なπ計算を学べます。
さらに数値計算に興味がある場合は、ガウス=ルジャンドル法を学ぶとよいでしょう。
確率や乱数を学びたい場合
確率や乱数、シミュレーションを学びたい場合は、モンテカルロ法が適しています。
モンテカルロ法は、πを効率よく高精度に求める方法ではありません。
しかし、ランダムな試行を大量に行い、統計的に近似値を得るという考え方を学ぶには非常にわかりやすい題材です。
高精度なπを求めたい場合
高精度なπを求めたい場合は、double では不十分です。
Boost.Multiprecisionのような多倍長計算ライブラリを使い、マチンの公式、ガウス=ルジャンドル法、チュドノフスキー法などと組み合わせる必要があります。
特に、数百桁以上のπを求めたい場合は、単純な実装ではなく、計算量やメモリ使用量にも配慮した方法が必要になります。
各方法の特徴
標準ライブラリのπ定数
標準ライブラリのπ定数は、実務用途に最も向いています。
自分でπを計算する必要がなく、コードの意図も明確です。
C++20以降を使える環境であれば、まずこの方法を選ぶのが自然です。
逆余弦関数を使う方法
逆余弦関数を使う方法は、C++20以前の環境で便利です。
数学的にも妥当で、古い環境でも比較的簡単にπを得られます。
ただし、C++20以降では標準定数を使う方が読みやすくなります。
ライプニッツ級数
ライプニッツ級数は、実装が簡単で学習向きです。
ただし、収束が非常に遅いため、実用的な高精度計算には向いていません。
ニラカンタ級数
ニラカンタ級数は、ライプニッツ級数よりも収束が速い方法です。
級数による近似の違いを学ぶには適していますが、超高精度計算を目的とする場合には、より効率のよい方法を使う方がよいでしょう。
モンテカルロ法
モンテカルロ法は、乱数を使ってπを近似する方法です。
確率やシミュレーションの学習には向いていますが、高精度なπ計算には向いていません。
実行するたびに結果が変わる点にも注意が必要です。
マチンの公式
マチンの公式は、実装のしやすさと収束の速さのバランスがよい方法です。
C++でπを自分で計算する題材としては、非常におすすめできます。
多倍長計算と組み合わせれば、高精度計算にも応用できます。
ガウス=ルジャンドル法
ガウス=ルジャンドル法は、非常に速くπに収束する方法です。
少ない反復で高精度に近づくため、数値計算の学習にも適しています。
ただし、高精度を活かすには多倍長型が必要です。
チュドノフスキー法
チュドノフスキー法は、超高精度計算向けの方法です。
収束は非常に速いものの、実装は複雑です。
初心者向けというより、発展的な数値計算や多倍長計算のテーマとして扱うのが適しています。
まとめ
C++でπを使うだけなら自分で計算しなくてよい
C++でπを使うだけなら、標準機能を使うのが最も安全です。
C++20以降なら標準ライブラリのπ定数を使い、C++20以前なら逆余弦関数を使ってπを得る方法があります。
実務では、これらの方法で十分な場合がほとんどです。
π計算を学ぶなら目的に応じて方法を選ぶ
アルゴリズムの学習としてπを計算するなら、まずはライプニッツ級数やニラカンタ級数から始めると理解しやすいです。
より効率的な方法を学びたい場合は、マチンの公式やガウス=ルジャンドル法に進むとよいでしょう。
確率的な近似を学びたい場合は、モンテカルロ法が適しています。
高精度計算では数値型が重要
高精度なπを求めるには、計算式だけでなく数値型にも注意が必要です。
double ではおよそ15〜17桁程度が限界です。
より多くの桁を正確に求めたい場合は、Boost.Multiprecisionのような多倍長計算ライブラリを使う必要があります。
目的に合った方法を選ぶことが大切
C++でπを扱う方法は、目的によって最適解が変わります。
実務で使うなら標準ライブラリ、学習目的なら級数や反復法、高精度計算なら多倍長計算と高速なアルゴリズムを組み合わせるのが適切です。
πの計算では、「どの方法が一番正しいか」ではなく、「何のためにπを求めるのか」に合わせて方法を選ぶことが重要です。
以上、C++でπ計算する方法についてでした。
最後までお読みいただき、ありがとうございました。
