现在的位置: 首页 > 综合 > 正文

计算圆周率的C#程序

2012年10月01日 ⁄ 综合 ⁄ 共 4576字 ⁄ 字号 评论关闭

关于圆周率,可参阅“维基百科-圆周率”。我前天在博客园也发表了一篇随笔:“计算圆周率的C程序” 。这个C#版的计算圆周率程序就是在C程序的基础上改写的。C#版的程序必须使用C#2.0编译,算法和C程序是一样的,都是利用圆周率的反正切展式的泰勒级数来计算,但C#程序充分使用面象对象的编程方法,并且程序中有适当的注释,比C程序容易理解多了。C#程序从配置文件中读取计算所用的公式,允许自己增加计算公式。

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Diagnostics;

namespace Skyiv.Util
{
  
// 用反正切展式计算圆周率
  
// 例如: pi= + 16 * arctan(1/5) - 4 * arctan(1/239) [Machin]
  sealed class ThePi
  
{
    
// 表示 (positive ? + : -) coefficient * arctan(1/denominator)
    public sealed class Term
    
{
      
bool positive;   // 系数是否正数
      int coefficient; // 系数
      int denominator; // 分母
      
      
public bool Positive get return positive; } }
      
public int Coefficient get return coefficient; } }
      
public int Denominator get return denominator; } }
      
      
public Term(bool positive, int coefficient, int denominator)
      
{
        
this.positive = positive;
        
this.coefficient = coefficient;
        
this.denominator = denominator;
      }

      
      
public override string ToString()
      
{
        
return string.Format("{0} {1} * arctan(1/{2}) ", positive ? "+" : "-", coefficient, denominator);
      }

    }

    
    
const int overDigits = 3;           // 额外计算的位数,以消除误差
    List<Term> list = new List<Term>(); // 反正切展式
    string name;                        // 反正切展式的发现者
    
    
public ThePi(string name)
    
{
      
this.name = name;
    }

    
    
public void Add(Term term)
    
{
      list.Add(term);
    }

    
    
// 返回反正切展式,例如: pi= + 16 * arctan(1/5) - 4 * arctan(1/239) [Machin]
    public override string ToString()
    
{
      
string s = "pi= ";
      
foreach (Term term in list) s += term.ToString();
      
return s + "[" + name + "]";
    }

    
    
// 将圆周率精确到小数点后 digits 位的值以及所用时间输出到 tw
    public void Out(TextWriter tw, int digits)
    
{
      
const int digitsPerGroup = 5;
      
const int groupsPerLine = 13;
      
const int digitsPerLine = groupsPerLine * digitsPerGroup;
      tw.WriteLine(
this);
      TimeSpan elapsed;
      
int [] piValue = Compute(digits, out elapsed);
      tw.Write(
"pi= {0}.", piValue[piValue.Length - 1]);
      
int position = 6;
      
for (int i = piValue.Length - 2; i >= overDigits; i--, position++)
      
{
        tw.Write(piValue[i]);
        
if (position % digitsPerLine == 0) tw.WriteLine();
        
else if (position % digitsPerGroup == 0) tw.Write(" ");
      }

      
if ((position - 1% digitsPerLine != 0) tw.WriteLine();
      tw.WriteLine(
"[{0}] DIGITS:{1:N0} ELAPSED:{2}", name, digits, elapsed);
    }

      
    
// 计算圆周率到小数点后 digits 位, 并统计所用时间 elapsed
    int [] Compute(int digits, out TimeSpan elapsed)
    
{
      Stopwatch stopWatch 
= new Stopwatch();
      stopWatch.Start();
      
int [] piValue = Compute(digits + overDigits);
      Format(piValue);
      stopWatch.Stop();
      elapsed 
= stopWatch.Elapsed;
      
return piValue;
    }

    
    
// 计算圆周率到小数点后 digits 位, 结果未格式化
    int [] Compute(int digits)
    
{
      
int [] pi  = new int [digits + 1]; // 圆周率的值, 反序存放,如: 951413
      int [] tmp = new int [digits + 1]; // 中间计算结果,也是反序存放
      foreach (Term term in list)
      
{
        
// arctan(x) = x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - x^11/11 + ..
        int validDigits = digits;
        
int divisor = term.Denominator;
        
bool positive = term.Positive;
        Array.Clear(tmp, 
0, tmp.Length);
        tmp[digits] 
= term.Coefficient;
        Divide(
true, positive, trueref validDigits, pi, tmp, divisor);
        positive 
= !positive;
        divisor 
*= divisor;
        
for (int step = 3; validDigits > 0; positive = !positive, step += 2)
        
{
          Divide(
falsetruetrueref validDigits, null, tmp, divisor);
          Divide(
true, positive, falseref validDigits, pi, tmp, step);
        }

      }

      
return pi;
    }


    
// 计算 sum += 或 -= (dividend /= 或 / divisor)
    void Divide(
      
bool updateSum,      // 是否更新sum
      bool positive,       // 系数是否正数
      bool updateDividend, // 是否更新被除数
      ref int digits,      // 被除数的有效位数
      int [] sum,          // 和数
      int [] dividend,     // 被除数
      int divisor          // 除数
      )
    
{
      
for (int remainder = 0, i = digits; i >= 0; i--)
      
{
        
int quotient = 10 * remainder + dividend[i];
        remainder 
= quotient % divisor;
        quotient 
/= divisor;
        
if (updateDividend) dividend[i] = quotient;
        
if (!updateSum) continue;
        
if (positive) sum[i] += quotient;
        
else sum[i] -= quotient;
      }

      
if (updateDividend) while (digits > 0 && dividend[digits] == 0) digits--;
    }


    
// 将 pi 数据组中的每个元素格式化为个位数
    void Format(int [] pi)
    
{
      
for (int quotient = 0, i = 0; i < pi.Length; i++)
      
{

抱歉!评论已关闭.