Beispiel #1
0
        /// <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;
        }