关于圆周率,可参阅“维基百科-圆周率”。我前天在博客园也发表了一篇随笔:“计算圆周率的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, true, ref validDigits, pi, tmp, divisor);
positive = !positive;
divisor *= divisor;
for (int step = 3; validDigits > 0; positive = !positive, step += 2)
{
Divide(false, true, true, ref validDigits, null, tmp, divisor);
Divide(true, positive, false, ref 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++)
{
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, true, ref validDigits, pi, tmp, divisor);
positive = !positive;
divisor *= divisor;
for (int step = 3; validDigits > 0; positive = !positive, step += 2)
{
Divide(false, true, true, ref validDigits, null, tmp, divisor);
Divide(true, positive, false, ref 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++)
{