.net 如何在C#中使用循环计算pi到N位数

tpgth1q7  于 2022-12-14  发布在  .NET
关注(0)|答案(6)|浏览(153)

在C#中,我应该如何计算PI到一定的小数位数?
我希望能够将一个数字传递给一个方法,然后返回计算到小数位数的PI。

public decimal CalculatePi(int places)
{
    // magic
    return pi;
}

Console.WriteLine(CalculatePi(5)); // Would print 3.14159

Console.WriteLine(CalculatePi(10)); // Would print 3.1415926535

等等。
我不在乎程序的速度。我只希望它尽可能简单易懂。提前感谢你的帮助。

gcmastyq

gcmastyq1#

首先,假设你想要任意位数的pi,并且我们不想被限制在任何一个浮点数的精度上,让我们把一个Pi函数定义为一个字符串,而不是任何数字类型。
我在寻找这种技术时发现的最酷的算法之一是Stanley Rabinowitz and Stan Wagon - Spigot Algorithm,它不需要浮点运算,主要是一种迭代方法,但在中间计算中需要一些内存来存储整数数组。
这里没有花时间简化或清理代码,而是实现了该算法(注意,结果没有添加小数点)。
请务必引用算法和本网站如果您打算使用此代码以外的任何个人使用。

C#代码

public static string CalculatePi(int digits)
{   
    digits++;

    uint[] x = new uint[digits*10/3+2];
    uint[] r = new uint[digits*10/3+2];
    
    uint[] pi = new uint[digits];

    for (int j = 0; j < x.Length; j++)
        x[j] = 20;
        
    for (int i = 0; i < digits; i++)
    {
        uint carry = 0;
        for (int j = 0; j < x.Length; j++)
        {
            uint num = (uint)(x.Length - j - 1);
            uint dem = num * 2 + 1;

            x[j] += carry;

            uint q = x[j] / dem;
            r[j] = x[j] % dem;

            carry = q * num;
        }
        
        
        pi[i] = (x[x.Length-1] / 10);
            
                    
        r[x.Length - 1] = x[x.Length - 1] % 10; ;
        
        for (int j = 0; j < x.Length; j++)
            x[j] = r[j] * 10;
    }
    
    var result = "";
    
    uint c = 0;
    
    for(int i = pi.Length - 1; i >=0; i--)
    {
        pi[i] += c;
        c = pi[i] / 10;
        
        result = (pi[i] % 10).ToString() + result;
    }

    return result;
}

更新

我终于开始修复35位之后发生的“进位错误”。链接文档的第6页,事实上,专门谈到了这里发生的事情。我已经测试了最终版本好到1000位。

6ss1mwsb

6ss1mwsb2#

Math.Round(Math.PI, places)

如果你需要更高的精度,你会在使用double数据类型时遇到麻烦,因为它支持一个特定的最大精度(由Math.PI提供)。

k3bvogb1

k3bvogb13#

经过大量的搜索,我发现了这个小片段:

public static class BigMath
{
    // digits = number of digits to calculate;
    // iterations = accuracy (higher the number the more accurate it will be and the longer it will take.)
    public static BigInteger GetPi(int digits, int iterations)
    {
        return 16 * ArcTan1OverX(5, digits).ElementAt(iterations)
            - 4 * ArcTan1OverX(239, digits).ElementAt(iterations);
    }

    //arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
    public static IEnumerable<BigInteger> ArcTan1OverX(int x, int digits)
    {
        var mag = BigInteger.Pow(10, digits);
        var sum = BigInteger.Zero;
        bool sign = true;
        for (int i = 1; true; i += 2)
        {
            var cur = mag / (BigInteger.Pow(x, i) * i);
            if (sign)
            {
                sum += cur;
            }
            else
            {
                sum -= cur;
            }
            yield return sum;
            sign = !sign;
        }
    }
}

到目前为止,它的工作非常有魅力。您只需要从GAC添加System.Numerics库就可以解析BigInteger类型。

x6492ojm

x6492ojm4#

与nicholas相同的算法,但使用yield进行延迟计算

static public IEnumerable<uint> Pi()
    {
        uint[] x = new uint[short.MaxValue];
        uint[] r = new uint[short.MaxValue];

        for (int j = 0; j < short.MaxValue; j++)
            x[j] = 20;

        for (int i = 0; i < short.MaxValue; i++)
        {
            uint carry = 0;
            for (int j = 0; j < x.Length; j++)
            {
                uint num = (uint)(x.Length - j - 1);
                uint dem = num * 2 + 1;

                x[j] += carry;

                uint q = x[j] / dem;
                r[j] = x[j] % dem;

                carry = q * num;
            }

            yield return (x[x.Length - 1] / 10);

            r[x.Length - 1] = x[x.Length - 1] % 10; ;
            for (int j = 0; j < x.Length; j++)
            {
                x[j] = r[j] * 10;
            }                    
        }
    }

我使用short.MaxValue作为位置数的上限,但这是因为我的机器虚拟内存不足,更好的机器应该能够容纳最多int.MaxValue。
该函数可以这样调用:

class Program
{
    static void Main(string[] args)
    {
        foreach (uint digit in Calculator.Pi().Take(100))
        {
            Console.WriteLine(digit);
        }

        Console.Read();
    }
}
pjngdqdw

pjngdqdw5#

如果您对本机数学库提供的位数感到满意,那么它就很简单;如果你需要更多的位数(几十位、几百位或几千位),你需要一个一次吐出一位数的spigot算法。Jeremy Gibbons在我的博客上给出了一个algorithm,我implementtwice,在我的博客上你可以找到Scheme、C、Python、Haskell、Perl和Forth(抱歉,不是C#)的代码。

xlpyo6sf

xlpyo6sf6#

最简单的方法是将大量的数字pi存储在一个String常量中,然后当你需要n位数精度时,你只需要从0到n+2取一个子串。

相关问题