BigNumbers in C# QuickStart Sample
Illustrates the basic use of the arbitrary precision classes: BigInteger, BigRational, BigFloat in C#.
View this sample in: Visual Basic F# IronPython
using System;
// The arbitrary precision types reside in the Numerics.NET
// namespace.
using Numerics.NET;
namespace Numerics.NET.QuickStart.CSharp
{
/// <summary>
/// Illustrates the use of the arbitrary precision number
/// classes in Numerics.NET.
/// </summary>
class BigNumbers
{
static void Main(string[] args)
{
// The license is verified at runtime. We're using
// a 30 day trial key here. For more information, see
// https://numerics.net/trial-key
Numerics.NET.License.Verify("64542-18980-57619-62268");
// In this QuickStart sample, we'll use 5 different methods to compute
// the value of pi, the ratio of the circumference to the diameter of
// a circle.
BigFloat pi;
// The number of decimal digits.
int digits = 10000;
// The equivalent number of binary digits, to account for round-off error:
int binaryDigits = (int)(8 + digits * Math.Log(10, 2));
// The number of digits in the last correction, if applicable.
double correctionDigits;
// First, create an AccuracyGoal for the number of digits we want.
// We'll add 5 extra digits to account for round-off error.
AccuracyGoal goal = AccuracyGoal.Absolute(digits + 5);
Console.WriteLine($"Calculating {digits} digits of pi:");
// Create a stopwatch so we can time the results.
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
//
// Method 1: Arctan formula
//
// pi/4 = 88L(172) + 51L(239) + 32L(682) + 44L(5357) + 68L(12943)
// Where L(p) = Arctan(1/p)
// We will use big integer arithmetic for this.
// See the helper function Arctan later in this file.
Console.WriteLine("Arctan formula using integer arithmetic:");
sw.Start();
int[] coefficients = { 88, 51, 32, 44, 68 };
int[] arguments = { 172, 239, 682, 5357, 12943 };
pi = BigFloat.Zero;
for (int k = 0; k < 5; k++)
{
pi += coefficients[k] * Arctan(arguments[k], binaryDigits);
Console.WriteLine("Step {0}: ({1:F3} seconds)", k + 1, sw.Elapsed.TotalSeconds);
}
// The ScaleByPowerOfTwo is the fastest way to multiply
// or divide by a power of two:
pi = BigFloat.ScaleByPowerOfTwo(pi, 2);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
Console.WriteLine();
//
// Method 2: Rational approximation
//
// pi/2 = 1 + 1/3 + (1*2)/(3*5) + (1*2*3)/(3*5*7) + ...
// = 1 + 1/3 * (1 + 2/5 * (1 + 3/7 * (1 + ...)))
// We gain 1 bit per iteration, so we know where to start.
Console.WriteLine("Rational approximation using rational arithmetic.");
Console.WriteLine("This is very inefficient, so we only do up to 10000 digits.");
sw.Start();
BigRational an = BigRational.Zero;
int n0 = digits <= 10000 ? binaryDigits : (int)(8 + 10000 * Math.Log(10, 2));
for (int n = n0; n > 0; n--)
an = new BigRational(n, 2 * n + 1) * an + 1;
pi = new BigFloat(2 * an, goal, RoundingMode.TowardsNearest);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
Console.WriteLine();
//
// Method 3: Arithmetic-Geometric mean
//
// By Salamin & Brent, based on discoveries by C.F.Gauss.
// See http://www.cs.miami.edu/~burt/manuscripts/gaussagm/agmagain-hyperref.pdf
Console.WriteLine("Arithmetic-Geometric Mean:");
sw.Reset();
sw.Start();
BigFloat x1 = BigFloat.Sqrt(2, goal, RoundingMode.TowardsNearest);
BigFloat x2 = BigFloat.One;
BigFloat S = BigFloat.Zero;
BigFloat c = BigFloat.One;
for (int k = 0; k < int.MaxValue; k++)
{
S += BigFloat.ScaleByPowerOfTwo(c, k - 1);
BigFloat aMean = BigFloat.ScaleByPowerOfTwo(x1 + x2, -1);
BigFloat gMean = BigFloat.Sqrt(x1 * x2);
x1 = aMean;
x2 = gMean;
c = (x1 + x2) * (x1 - x2);
// GetDecimalDigits returns the approximate number of digits in a number.
// A negative return value means the number is less than 1.
correctionDigits = -c.GetDecimalDigits();
Console.WriteLine("Iteration {0}: {1:F1} digits ({2:F3} seconds)", k, correctionDigits, sw.Elapsed.TotalSeconds);
if (correctionDigits >= digits)
break;
}
pi = x1 * x1 / (1 - S);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
Console.WriteLine();
//
// Method 4: Borweins' quartic formula
//
// This algorithm quadruples the number of correct digits
// in each iteration.
// See http://en.wikipedia.org/wiki/Borwein's_algorithm
Console.WriteLine("Quartic formula:");
sw.Reset();
sw.Start();
BigFloat sqrt2 = BigFloat.Sqrt(2, goal, RoundingMode.TowardsNearest);
BigFloat y = sqrt2 - BigFloat.One;
BigFloat a = new BigFloat(6, goal) - BigFloat.ScaleByPowerOfTwo(sqrt2, 2);
BigFloat y2 = y * y, y3, y4 = y2 * y2;
BigFloat da;
correctionDigits = 0;
for (int k = 1; 4 * correctionDigits < digits; k++)
{
BigFloat qrt = BigFloat.Root(1 - y4, 4);
y = BigFloat.Subtract(1, qrt, goal, RoundingMode.TowardsNearest)
/ BigFloat.Add(1, qrt, goal, RoundingMode.TowardsNearest);
// y = BigFloat.Divide(1 - qrt, 1 + qrt, AccuracyGoal.InheritAbsolute, RoundingMode.TowardsNearest);
y2 = y * y;
y3 = y * y2;
y4 = y2 * y2;
da = (BigFloat.ScaleByPowerOfTwo(y + y3, 2) + (6 * y2 + y4)) * a
- BigFloat.ScaleByPowerOfTwo(y + y2 + y3, 2 * k + 1);
da = da.RestrictPrecision(goal, RoundingMode.TowardsNearest);
a += da;
correctionDigits = -da.GetDecimalDigits();
Console.WriteLine("Iteration {0}: {1:F1} digits ({2:F3} seconds)", k, correctionDigits, sw.Elapsed.TotalSeconds);
}
pi = BigFloat.Inverse(a);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
Console.WriteLine();
//
// Method 5: The built-in method
//
// The method used to compute pi internally is an order of magnitude
// faster than any of the above.
Console.WriteLine("Built-in function:");
sw.Reset();
sw.Start();
pi = BigFloat.GetPi(goal);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
// The highest precision value of pi is cached, so
// getting pi to any precision up to that is super fast.
Console.WriteLine("Built-in function (cached):");
sw.Reset();
sw.Start();
pi = BigFloat.GetPi(goal);
sw.Stop();
Console.WriteLine("Total time: {0:F3} seconds.", sw.Elapsed.TotalSeconds, pi);
Console.Write("Press Enter key to exit...");
Console.ReadLine();
}
/// <summary>
/// Helper function to compute Arctan(1/p)
/// </summary>
/// <param name="p">The reciprocal of the argument.</param>
/// <param name="binaryDigits">The number of binary digits in the result.</param>
/// <returns>Arctan(1/<paramref name="p"/>) to <paramref name="binaryDigits"/> binary digits.</returns>
static BigFloat Arctan(int p, int binaryDigits)
{
// We scale the result by a factor of 2^binaryDigits.
// The first term is 1/p.
BigInteger power = BigInteger.Pow(2, binaryDigits) / p;
// We store the sum in result.
BigInteger result = power;
bool subtract = true;
int k = 0;
while (!power.IsZero)
{
k++;
// Expressions involving big integers look exactly like any other arithmetic expression:
// The kth term is (-1)^k 1/(2k+1) 1/p^2k.
// So the power is 1/p^2 times the previous power.
power /= (p * p);
// And we alternately add and subtract
if (subtract)
result -= power / (2 * k + 1);
else
result += power / (2 * k + 1);
subtract = !subtract;
}
// Scale the result.
return BigFloat.ScaleByPowerOfTwo(new BigFloat(result), -binaryDigits);
}
}
}