/// <summary> /// Division (this = this / n2) /// </summary> /// <param name="n2">The number to divide this by</param> public void Div(BigFloat n2) { if (SpecialValueDivTest(n2)) return; if (mantissa.Precision.NumBits >= 8192) { BigFloat rcp = n2.Reciprocal(); Mul(rcp); } else { int shift = mantissa.DivAndShift(n2.mantissa); exponent = exponent - (n2.exponent + shift); } }
/// <summary> /// Calculates the odd reciprocals of the natural numbers (for atan series) /// </summary> /// <param name="numBits"></param> /// <param name="terms"></param> private static void CalculateReciprocals(int numBits, int terms) { int bits = numBits + 32; PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); System.Collections.Generic.List<BigFloat> list = new System.Collections.Generic.List<BigFloat>(terms); for (int i = 0; i < terms; i++) { BigFloat term = new BigFloat(i*2 + 1, extendedPres); list.Add(new BigFloat(term.Reciprocal(), normalPres)); } reciprocals = list.ToArray(); }
/// <summary> /// Constructs a BigFloat from a string /// </summary> /// <param name="value"></param> /// <param name="mantissaPrec"></param> public BigFloat(string value, PrecisionSpec mantissaPrec) { Init(mantissaPrec); PrecisionSpec extendedPres = new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN); BigFloat ten = new BigFloat(10, extendedPres); BigFloat iPart = new BigFloat(extendedPres); BigFloat fPart = new BigFloat(extendedPres); BigFloat tenRCP = ten.Reciprocal(); if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NaNSymbol)) { SetNaN(); return; } else if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PositiveInfinitySymbol)) { SetInfPlus(); return; } else if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeInfinitySymbol)) { SetInfMinus(); return; } string decimalpoint = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; char[] digitChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '.' }; //Read in the integer part up the the decimal point. bool sign = false; value = value.Trim(); int i = 0; if (value.Length > i && value[i] == '-') { sign = true; i++; } if (value.Length > i && value[i] == '+') { i++; } for ( ; i < value.Length; i++) { //break on decimal point if (value[i] == decimalpoint[0]) break; int digit = Array.IndexOf(digitChars, value[i]); if (digit < 0) break; //Ignore place separators (assumed either , or .) if (digit > 9) continue; if (i > 0) iPart.Mul(ten); iPart.Add(new BigFloat(digit, extendedPres)); } //If we've run out of characters, assign everything and return if (i == value.Length) { iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; return; } //Assign the characters after the decimal point to fPart if (value[i] == '.' && i < value.Length - 1) { BigFloat RecipToUse = new BigFloat(tenRCP); for (i++; i < value.Length; i++) { int digit = Array.IndexOf(digitChars, value[i]); if (digit < 0) break; BigFloat temp = new BigFloat(digit, extendedPres); temp.Mul(RecipToUse); RecipToUse.Mul(tenRCP); fPart.Add(temp); } } //If we're run out of characters, add fPart and iPart and return if (i == value.Length) { iPart.Add(fPart); iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; return; } if (value[i] == '+' || value[i] == '-') i++; if (i == value.Length) { iPart.Add(fPart); iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; return; } //Look for exponential notation. if ((value[i] == 'e' || value[i] == 'E') && i < value.Length - 1) { //Convert the exponent to an int. int exp; try { exp = System.Convert.ToInt32(new string(value.ToCharArray(i + 1, value.Length - (i + 1)))); } catch (Exception) { iPart.Add(fPart); iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; return; } //Raise or lower 10 to the power of the exponent BigFloat acc = new BigFloat(1, extendedPres); BigFloat temp = new BigFloat(1, extendedPres); int powerTemp = exp; BigFloat multiplierToUse; if (exp < 0) { multiplierToUse = new BigFloat(tenRCP); powerTemp = -exp; } else { multiplierToUse = new BigFloat(ten); } //Fast power function while (powerTemp != 0) { temp.Mul(multiplierToUse); multiplierToUse.Assign(temp); if ((powerTemp & 1) != 0) { acc.Mul(temp); } powerTemp >>= 1; } iPart.Add(fPart); iPart.Mul(acc); iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; return; } iPart.Add(fPart); iPart.mantissa.Sign = sign; exponent = iPart.exponent; if (mantissa.AssignHigh(iPart.mantissa)) exponent++; }
private static void CalculateEOnly(int numBits) { PrecisionSpec extendedPrecision = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); PrecisionSpec normalPrecision = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); int iExponent = (int)(Math.Sqrt(numBits)); BigFloat factorial = new BigFloat(1, extendedPrecision); BigFloat constant = new BigFloat(1, extendedPrecision); constant.exponent -= iExponent; BigFloat numerator = new BigFloat(constant); BigFloat reciprocal; //Calculate the 2^iExponent th root of e BigFloat e = new BigFloat(1, extendedPrecision); int i; for (i = 1; i < Int32.MaxValue; i++) { BigFloat number = new BigFloat(i, extendedPrecision); factorial.Mul(number); reciprocal = factorial.Reciprocal(); reciprocal.Mul(numerator); if (-reciprocal.exponent > numBits) break; e.Add(reciprocal); numerator.Mul(constant); System.GC.Collect(); } for (i = 0; i < iExponent; i++) { numerator.Assign(e); e.Mul(numerator); } //Set the cached static values. eCache = new BigFloat(e, normalPrecision); eRCPCache = new BigFloat(e.Reciprocal(), normalPrecision); }
/// <summary> /// Uses the Gauss-Legendre formula for pi /// Taken from http://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_algorithm /// </summary> /// <param name="numBits"></param> private static void CalculatePi(int numBits) { int bits = numBits + 32; //Precision extend taken out. PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); if (scratch.Precision.NumBits != bits) { scratch = new BigInt(extendedPres); } //a0 = 1 BigFloat an = new BigFloat(1, extendedPres); //b0 = 1/sqrt(2) BigFloat bn = new BigFloat(2, extendedPres); bn.Sqrt(); bn.exponent--; //to = 1/4 BigFloat tn = new BigFloat(1, extendedPres); tn.exponent -= 2; int pn = 0; BigFloat anTemp = new BigFloat(extendedPres); int iteration = 0; int cutoffBits = numBits >> 5; for (iteration = 0; ; iteration++) { //Save a(n) anTemp.Assign(an); //Calculate new an an.Add(bn); an.exponent--; //Calculate new bn bn.Mul(anTemp); bn.Sqrt(); //Calculate new tn anTemp.Sub(an); anTemp.mantissa.SquareHiFast(scratch); anTemp.exponent += anTemp.exponent + pn + 1 - anTemp.mantissa.Normalise(); tn.Sub(anTemp); anTemp.Assign(an); anTemp.Sub(bn); if (anTemp.exponent < -(bits - cutoffBits)) break; //New pn pn++; } an.Add(bn); an.mantissa.SquareHiFast(scratch); an.exponent += an.exponent + 1 - an.mantissa.Normalise(); tn.exponent += 2; an.Div(tn); pi = new BigFloat(an, normalPres); piBy2 = new BigFloat(pi); piBy2.exponent--; twoPi = new BigFloat(pi, normalPres); twoPi.exponent++; piRecip = new BigFloat(an.Reciprocal(), normalPres); twoPiRecip = new BigFloat(piRecip); twoPiRecip.exponent--; //1/3 is going to be useful for sin. threeRecip = new BigFloat((new BigFloat(3, extendedPres)).Reciprocal(), normalPres); }
private static void CalculateFactorials(int numBits) { System.Collections.Generic.List<BigFloat> list = new System.Collections.Generic.List<BigFloat>(64); System.Collections.Generic.List<BigFloat> list2 = new System.Collections.Generic.List<BigFloat>(64); PrecisionSpec extendedPrecision = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); PrecisionSpec normalPrecision = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); BigFloat factorial = new BigFloat(1, extendedPrecision); BigFloat reciprocal; //Calculate e while we're at it BigFloat e = new BigFloat(1, extendedPrecision); list.Add(new BigFloat(factorial, normalPrecision)); for (int i = 1; i < Int32.MaxValue; i++) { BigFloat number = new BigFloat(i, extendedPrecision); factorial.Mul(number); if (factorial.exponent > numBits) break; list2.Add(new BigFloat(factorial, normalPrecision)); reciprocal = factorial.Reciprocal(); e.Add(reciprocal); list.Add(new BigFloat(reciprocal, normalPrecision)); } //Set the cached static values. inverseFactorialCache = list.ToArray(); factorialCache = list2.ToArray(); invFactorialCutoff = numBits; eCache = new BigFloat(e, normalPrecision); eRCPCache = new BigFloat(e.Reciprocal(), normalPrecision); }
private static BigFloat R(BigFloat a0, BigFloat b0) { //Precision extend taken out. int bits = a0.mantissa.Precision.NumBits; PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); BigFloat an = new BigFloat(a0, extendedPres); BigFloat bn = new BigFloat(b0, extendedPres); BigFloat sum = new BigFloat(extendedPres); BigFloat term = new BigFloat(extendedPres); BigFloat temp1 = new BigFloat(extendedPres); BigFloat one = new BigFloat(1, extendedPres); int iteration = 0; for (iteration = 0; ; iteration++) { //Get the sum term for this iteration. term.Assign(an); term.Mul(an); temp1.Assign(bn); temp1.Mul(bn); //term = an^2 - bn^2 term.Sub(temp1); //term = 2^(n-1) * (an^2 - bn^2) term.exponent += iteration - 1; sum.Add(term); if (term.exponent < -(bits - 8)) break; //Calculate the new AGM estimates. temp1.Assign(an); an.Add(bn); //a(n+1) = (an + bn) / 2 an.MulPow2(-1); //b(n+1) = sqrt(an*bn) bn.Mul(temp1); bn.Sqrt(); } one.Sub(sum); one = one.Reciprocal(); return new BigFloat(one, a0.mantissa.Precision); }
private static BigFloat TenPow(int power, PrecisionSpec precision) { BigFloat acc = new BigFloat(1, precision); BigFloat temp = new BigFloat(1, precision); int powerTemp = power; BigFloat multiplierToUse = new BigFloat(10, precision); if (power < 0) { multiplierToUse = multiplierToUse.Reciprocal(); powerTemp = -power; } //Fast power function while (powerTemp != 0) { temp.Mul(multiplierToUse); multiplierToUse.Assign(temp); if ((powerTemp & 1) != 0) { acc.Mul(temp); } powerTemp >>= 1; } return acc; }
/// <summary> /// Calculates ln(2) and returns -10^(n/2 + a bit) for reuse, using the AGM method as described in /// http://lacim.uqam.ca/~plouffe/articles/log2.pdf /// </summary> /// <param name="numBits"></param> /// <returns></returns> private static void CalculateLog2(int numBits) { //Use the AGM method formula to get log2 to N digits. //R(a0, b0) = 1 / (1 - Sum(2^-n*(an^2 - bn^2))) //log(1/2) = R(1, 10^-n) - R(1, 10^-n/2) PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); PrecisionSpec extendedPres = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); BigFloat a0 = new BigFloat(1, extendedPres); BigFloat b0 = TenPow(-(int)((double)((numBits >> 1) + 2) * 0.302), extendedPres); BigFloat pow10saved = new BigFloat(b0); BigFloat firstAGMcacheSaved = new BigFloat(extendedPres); //save power of 10 (in normal precision) pow10cache = new BigFloat(b0, normalPres); ln2cache = R(a0, b0); //save the first half of the log calculation firstAGMcache = new BigFloat(ln2cache, normalPres); firstAGMcacheSaved.Assign(ln2cache); b0.MulPow2(-1); ln2cache.Sub(R(a0, b0)); //Convert to log(2) ln2cache.mantissa.Sign = false; //Save magic constant for newton log //First guess in range 1 <= x < 2 is x0 = ln2 * (x - 1) + C logNewtonConstant = new BigFloat(ln2cache); logNewtonConstant.Mul(new BigFloat(3, extendedPres)); logNewtonConstant.exponent--; logNewtonConstant.Sub(new BigFloat(1, extendedPres)); logNewtonConstant = new BigFloat(logNewtonConstant, normalPres); //Save the inverse. log2ecache = new BigFloat(ln2cache); log2ecache = new BigFloat(log2ecache.Reciprocal(), normalPres); //Now cache log10 //Because the log functions call this function to the precision to which they //are called, we cannot call them without causing an infinite loop, so we need //to inline the code. log10recip = new BigFloat(10, extendedPres); { int power2 = log10recip.exponent + 1; log10recip.exponent = -1; //BigFloat res = new BigFloat(firstAGMcache); BigFloat ax = new BigFloat(1, extendedPres); BigFloat bx = new BigFloat(pow10saved); bx.Mul(log10recip); BigFloat r = R(ax, bx); log10recip.Assign(firstAGMcacheSaved); log10recip.Sub(r); ax.Assign(ln2cache); ax.Mul(new BigFloat(power2, log10recip.mantissa.Precision)); log10recip.Add(ax); } log10recip = log10recip.Reciprocal(); log10recip = new BigFloat(log10recip, normalPres); //Trim to n bits ln2cache = new BigFloat(ln2cache, normalPres); }
/// <summary> /// Returns a base-10 string representing the number. /// /// Note: This is inefficient and possibly inaccurate. Please use with enough /// rounding digits (set using the RoundingDigits property) to ensure accuracy /// </summary> public override string ToString() { if (IsSpecialValue) { SpecialValueType s = SpecialValue; if (s == SpecialValueType.ZERO) { return String.Format("0{0}0", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); } else if (s == SpecialValueType.INF_PLUS) { return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PositiveInfinitySymbol; } else if (s == SpecialValueType.INF_MINUS) { return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeInfinitySymbol; } else if (s == SpecialValueType.NAN) { return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NaNSymbol; } else { return "Unrecognised special type"; } } if (scratch.Precision.NumBits != mantissa.Precision.NumBits) { scratch = new BigInt(mantissa.Precision); } //The mantissa expresses 1.xxxxxxxxxxx //The highest possible value for the mantissa without the implicit 1. is 0.9999999... scratch.Assign(mantissa); //scratch.Round(3); scratch.Sign = false; BigInt denom = new BigInt("0", mantissa.Precision); denom.SetBit(mantissa.Precision.NumBits - 1); bool useExponentialNotation = false; int halfBits = mantissa.Precision.NumBits / 2; if (halfBits > 60) halfBits = 60; int precDec = 10; if (exponent > 0) { if (exponent < halfBits) { denom.RSH(exponent); } else { useExponentialNotation = true; } } else if (exponent < 0) { int shift = -(exponent); if (shift < precDec) { scratch.RSH(shift); } else { useExponentialNotation = true; } } string output; if (useExponentialNotation) { int absExponent = exponent; if (absExponent < 0) absExponent = -absExponent; int powerOf10 = (int)((double)absExponent * Math.Log10(2.0)); //Use 1 extra digit of precision (this is actually 32 bits more, nb) BigFloat thisFloat = new BigFloat(this, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); thisFloat.mantissa.Sign = false; //Multiplicative correction factor to bring number into range. BigFloat one = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); BigFloat ten = new BigFloat(10, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); BigFloat tenRCP = ten.Reciprocal(); //Accumulator for the power of 10 calculation. BigFloat acc = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); BigFloat tenToUse; if (exponent > 0) { tenToUse = new BigFloat(tenRCP, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); } else { tenToUse = new BigFloat(ten, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); } BigFloat tenToPower = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); int powerTemp = powerOf10; //Fast power function while (powerTemp != 0) { tenToPower.Mul(tenToUse); tenToUse.Assign(tenToPower); if ((powerTemp & 1) != 0) { acc.Mul(tenToPower); } powerTemp >>= 1; } thisFloat.Mul(acc); //If we are out of range, correct. if (thisFloat.GreaterThan(ten)) { thisFloat.Mul(tenRCP); if (exponent > 0) { powerOf10++; } else { powerOf10--; } } else if (thisFloat.LessThan(one)) { thisFloat.Mul(ten); if (exponent > 0) { powerOf10--; } else { powerOf10++; } } //Restore the precision and the sign. BigFloat printable = new BigFloat(thisFloat, mantissa.Precision); printable.mantissa.Sign = mantissa.Sign; output = printable.ToString(); if (exponent < 0) powerOf10 = -powerOf10; output = String.Format("{0}E{1}", output, powerOf10); } else { BigInt bigDigit = BigInt.Div(scratch, denom); bigDigit.Sign = false; scratch.Sub(BigInt.Mul(denom, bigDigit)); if (mantissa.Sign) { output = String.Format("-{0}.", bigDigit); } else { output = String.Format("{0}.", bigDigit); } denom = BigInt.Div(denom, 10u); while (!denom.IsZero()) { uint digit = (uint)BigInt.Div(scratch, denom); if (digit == 10) digit--; scratch.Sub(BigInt.Mul(denom, digit)); output = String.Format("{0}{1}", output, digit); denom = BigInt.Div(denom, 10u); } output = RoundString(output, RoundingDigits); } return output; }