C# でぱい

前回のコードを C# で書いてみようと何故か思った。

書いていて気がついたのだが C# は百桁程度の整数も扱えない。
そういえば C++ も扱えない、今まで int64 までしか使ったこともない。

あぁ GMP ってそのことだったんだ。
Python や Ruby の整数は桁数が無限なので気がつかなかった。
てゆーか無限に扱える意味ってようするに数学の世界だと。

多倍長整数っていうんだね。
ググると BigInteger というのが見つかった。

CodeProject: C# BigInteger Class. Free source code and programming help

まあよく解らないものは使って覚えるのが一番早い。
クラスなので普通に new したあとは整数のように扱えるはず。
落としてプロジェクトに含めてみる。

いきなり困った、べき乗 Math.Pow() は double しか扱えない。

いや、考えてみたら計算は Math でやらなきゃいけないわけではない。
自分で for ループさせても何も問題無い、ここまではすぐに思いついた。
でもそれをどう計算…つまらないことに詰まってしまったり。

なんとか百桁に成功したところで一万桁とかにすると例外スロー。

private const int maxLength = 70;

この数値が関係あるらしい、大きな桁にするときは増やして使う。
まあとりあえずそんなこんなで書いてみたコード。

using System;
using System.Diagnostics;

namespace pi1
{
    class Test
    {
        // System.Numeric.BigInteger はまだない
        BigInteger p, q;

        BigInteger expansion(int n)
        {
            BigInteger x, nn, c, s, k;
            x = new BigInteger(0);
            nn = new BigInteger(0);
            c = new BigInteger(0);
            s = new BigInteger(0);
            k = new BigInteger(0);
            x = (p * q) / n;
            nn = n * n;
            c = 1;
            s = x;
            k = 1;
            while (x > 0)
            {
                x = x / nn;
                k = k + 2;
                c = -c;
                s = s + c * (x / k);
            }
            return s;
        }

        public void pie()
        {
            Console.WriteLine("円周率を計算します。");
            Console.WriteLine("桁数を指定してください");
            int prsn = int.Parse(Console.ReadLine());
            p = new BigInteger(1);
            q = new BigInteger(1);

            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 1; i <= prsn; i++)
                p *= 10;
            for (int i = 1; i <= 10; i++)
                q *= 10;

            BigInteger y, u, v;
            y = new BigInteger(0);
            u = new BigInteger(0);
            v = new BigInteger(0);
            y = 4 * ( 12 * expansion(18) + 8 * expansion(57) - 5 * expansion(239) );
            y = y / q;
            u = y / p;
            v = y - u * p;
            sw.Stop();
            
            Console.WriteLine("{0}.{1}", u, v);
            Console.WriteLine("計算時間={0}", sw.ElapsedMilliseconds / 1000.0);
            Console.Read();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            t.pie();
        }
    }
}

初期化の引数は BigInteger でもいいはずだけど怖いので int に。
あまり意味は無い。

cs_pi

IronPython より十倍速いわな、BigInteger が早いのだろうか。
それよりやはり .NET もコンパイルしたほうが早いということだろうけど。

とはいえ CPython にはボロ負けのまま…
せめてマルチスレッド化したほうが…意味があるかは置いておいて。
実用アプリじゃ全く問題無いことだし。

FFTとAGMによる円周率計算プログラム

C 言語は面倒くさいので上記のとかを試す。
百万桁でたったの 39 秒だもの、この差をどうこうしようとか思わないわ。

手元に IronPython 1.1 が残っていたので前回のコードを試す。
0.098 秒…あれ?二倍も速くなった、DLR ってもしかして絶望的に遅い?