/// <summary> /// Constructs a BigFloat from a BigInt, using the specified precision /// </summary> /// <param name="value"></param> /// <param name="mantissaPrec"></param> public BigFloat(BigInt value, PrecisionSpec mantissaPrec) { if (value.IsZero()) { Init(mantissaPrec); SetZero(); return; } mantissa = new BigInt(value, mantissaPrec); exponent = BigInt.GetMSB(value); mantissa.Normalise(); }
/// <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; }
/// <summary> /// Calculates 'this'^power /// </summary> /// <param name="power"></param> public void Power(BigInt power) { if (power.IsZero() || power.sign) { Zero(); digitArray[0] = 1; return; } BigInt pow = new BigInt(power); BigInt temp = new BigInt(this); BigInt powTerm = new BigInt(this); pow.Decrement(); for (; !pow.IsZero(); pow.RSH(1)) { if ((pow.digitArray[0] & 1) == 1) { temp.Mul(powTerm); } powTerm.Square(); } Assign(temp); }
/// <summary> /// This function is used for floating-point division. /// </summary> /// <param name="n2"></param> //Given two numbers: // In floating point 1 <= a, b < 2, meaning that both numbers have their top bits set. // To calculate a / b, maintaining precision, we: // 1. Double the number of digits available to both numbers. // 2. set a = a * 2^d (where d is the number of digits) // 3. calculate the quotient a <- q: 2^(d-1) <= q < 2^(d+1) // 4. if a >= 2^d, s = 1, else s = 0 // 6. shift a down by s, and undo the precision extension // 7. return 1 - shift (change necessary to exponent) public int DivAndShift(BigInt n2) { if (n2.IsZero()) return -1; if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); int oldLength = digitArray.Length; //Double the number of digits, and shift a into the higher digits. Pad(); n2.Extend(); //Do the divide (at double precision, ouch!) Div(n2); //Shift down if 'this' >= 2^d int ret = 1; if (digitArray[oldLength] != 0) { RSH(1); ret--; } SetNumDigits(oldLength); n2.SetNumDigits(oldLength); return ret; }
//********************* ToString members ********************** /// <summary> /// Converts this to a string, in the specified base /// </summary> /// <param name="numberBase">the base to use (min 2, max 16)</param> /// <returns>a string representation of the number</returns> public string ToString(int numberBase) { char[] digitChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; string output = ""; BigInt clone = new BigInt(this); clone.sign = false; int numDigits = 0; while (!clone.IsZero()) { if (numberBase == 10 && (numDigits % 3) == 0 && numDigits != 0) { output = String.Format(",{0}", output); } else if (numberBase != 10 && (numDigits % 8) == 0 && numDigits != 0) { output = String.Format(" {0}", output); } BigInt div, mod; DivMod(clone, (uint)numberBase, out div, out mod); int iMod = (int)mod; output = String.Format("{0}{1}", digitChars[(int)mod], output); numDigits++; clone = div; } if (output.Length == 0) output = String.Format("0"); if (sign) output = String.Format("-{0}", output); return output; }
/// <summary> /// True iff n2 (precision-adjusted to this) == n1 /// </summary> /// <param name="n2"></param> /// <returns></returns> public bool Equals(BigInt n2) { if (IsZero() && n2.IsZero()) return true; if (sign != n2.sign) return false; int Length = digitArray.Length; if (n2.digitArray.Length != Length) MakeSafe(ref n2); for (int i = 0; i < Length; i++) { if (digitArray[i] != n2.digitArray[i]) return false; } return true; }