Ejemplo n.º 1
0
        private static bool DoubleToShortestAscii(ref GrisuDouble v, char[] buffer, out int length, out int point)
        {
            Debug.Assert(!v.IsSpecial);
            Debug.Assert(v.Value >= 0.0);

            double value = v.Value;

            if (value == 0.0)
            {
                buffer[0] = '0';
                buffer[1] = '\0';
                length    = 1;
                point     = 1;
                return(true);
            }

            int  decimal_exponent = 0;
            bool result           = Grisu3(ref v, buffer, out length, out decimal_exponent);

            if (result)
            {
                point = length + decimal_exponent;
            }
            else
            {
                point = 0;
            }
            return(result);
        }
Ejemplo n.º 2
0
 private static void HandleSpecialValues(
     ref GrisuDouble double_inspect,
     TextWriter resultBuilder)
 {
     if (double_inspect.IsInfinite)
     {
         if (double_inspect.Value < 0)
         {
             resultBuilder.Write('-');
         }
         resultBuilder.Write(infinity_symbol_);
         return;
     }
     if (double_inspect.IsNaN)
     {
         resultBuilder.Write(nan_symbol_);
         return;
     }
 }
Ejemplo n.º 3
0
        // Provides a decimal representation of v.
        // Returns true if it succeeds, otherwise the result cannot be trusted.
        // There will be *length digits inside the buffer (not null-terminated).
        // If the function returns true then
        //        v == (double) (buffer * 10^decimal_exponent).
        // The digits in the buffer are the shortest representation possible: no
        // 0.09999999999999999 instead of 0.1. The shorter representation will even be
        // chosen even if the longer one would be closer to v.
        // The last digit will be closest to the actual v. That is, even if several
        // digits might correctly yield 'v' when read again, the closest will be
        // computed.
        private static bool Grisu3(ref GrisuDouble v,
                           char[] buffer,
                           out int length,
                           out int decimal_exponent)
        {
            DiyFp w = v.AsNormalizedDiyFp();
            // boundary_minus and boundary_plus are the boundaries between v and its
            // closest floating-point neighbors. Any number strictly between
            // boundary_minus and boundary_plus will round to v when convert to a double.
            // Grisu3 will never output representations that lie exactly on a boundary.
            DiyFp boundary_minus, boundary_plus;
            v.NormalizedBoundaries(out boundary_minus, out boundary_plus);
            Debug.Assert(boundary_plus.E == w.E);
            DiyFp ten_mk;  // Cached power of ten: 10^-k
            int mk;        // -k
            int ten_mk_minimal_binary_exponent =
               kMinimalTargetExponent - (w.E + DiyFp.kSignificandSize);
            int ten_mk_maximal_binary_exponent =
               kMaximalTargetExponent - (w.E + DiyFp.kSignificandSize);
            PowersOfTenCache.GetCachedPowerForBinaryExponentRange(
                ten_mk_minimal_binary_exponent,
                ten_mk_maximal_binary_exponent,
                out ten_mk, out mk);
            Debug.Assert((kMinimalTargetExponent <= w.E + ten_mk.E +
                    DiyFp.kSignificandSize) &&
                   (kMaximalTargetExponent >= w.E + ten_mk.E +
                    DiyFp.kSignificandSize));
            // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
            // 64 bit significand and ten_mk is thus only precise up to 64 bits.

            // The DiyFp.Times procedure rounds its result, and ten_mk is approximated
            // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
            // off by a small amount.
            // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
            // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
            //           (f-1) * 2^e < w*10^k < (f+1) * 2^e
            //DiyFp scaled_w = DiyFp.Times(ref w, ref ten_mk);
            w.Multiply(ref ten_mk);
            Debug.Assert(w.E ==
                   boundary_plus.E + ten_mk.E + DiyFp.kSignificandSize);
            // In theory it would be possible to avoid some recomputations by computing
            // the difference between w and boundary_minus/plus (a power of 2) and to
            // compute scaled_boundary_minus/plus by subtracting/adding from
            // scaled_w. However the code becomes much less readable and the speed
            // enhancements are not terriffic.
            //DiyFp scaled_boundary_minus = DiyFp.Times(ref boundary_minus, ref ten_mk);
            boundary_minus.Multiply(ref ten_mk);
            //DiyFp scaled_boundary_plus = DiyFp.Times(ref boundary_plus, ref ten_mk);
            boundary_plus.Multiply(ref ten_mk);

            // DigitGen will generate the digits of scaled_w. Therefore we have
            // v == (double) (scaled_w * 10^-mk).
            // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
            // integer than it will be updated. For instance if scaled_w == 1.23 then
            // the buffer will be filled with "123" und the decimal_exponent will be
            // decreased by 2.
            int kappa;
            bool result = DigitGen(ref boundary_minus, ref w, ref boundary_plus,
                                   buffer, out length, out kappa);
            decimal_exponent = -mk + kappa;
            return result;
        }
Ejemplo n.º 4
0
        private static bool DoubleToShortestAscii(ref GrisuDouble v, char[] buffer, out int length, out int point)
        {
            Debug.Assert(!v.IsSpecial);
            Debug.Assert(v.Value >= 0.0);

            double value = v.Value;

            if (value == 0.0)
            {
                buffer[0] = '0';
                buffer[1] = '\0';
                length = 1;
                point = 1;
                return true;
            }

            int decimal_exponent = 0;
            bool result = Grisu3(ref v, buffer, out length, out decimal_exponent);
            if (result)
            {
                point = length + decimal_exponent;
            }
            else
            {
                point = 0;
            }
            return result;
        }
Ejemplo n.º 5
0
        public static void DoubleToString(double value, TextWriter resultBuilder)
        {
            if (value < 0.0)
            {
                resultBuilder.Write('-');
                value = -value;
            }

            GrisuDouble grisuDouble = new GrisuDouble(value);
            if (grisuDouble.IsSpecial)
            {
                HandleSpecialValues(ref grisuDouble, resultBuilder);
                return;
            }

            char[] decimal_rep = ts_decimal_rep;
            if (decimal_rep == null)
                decimal_rep = ts_decimal_rep = new char[kBase10MaximalLength + 1];

            int decimal_point;
            int decimal_rep_length;

            if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point))
            {
                resultBuilder.Write("{0:R}", value);
                return;
            }

            int decimalRepLength = decimal_rep_length + 1;
            if (decimal_point < 1)
            {
                decimalRepLength += -decimal_point + 1;
            }
            else if (decimal_point >= decimal_rep_length)
            {
                decimalRepLength += decimal_point - decimal_rep_length + 1;
            }

            int exponent = decimal_point - 1;
            int absExponent = Math.Abs(exponent);
            int exponentRepLength = decimal_rep_length + 3;
            if (exponent < 0)
                ++exponentRepLength;
            if (absExponent >= 10)
            {
                ++exponentRepLength;
                if (absExponent >= 100)
                    ++exponentRepLength;
            }

            if (decimalRepLength <= exponentRepLength)
            {
                CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
                                            decimal_point,
                                            Math.Max(0, decimal_rep_length - decimal_point),
                                            resultBuilder);
            }
            else
            {
                CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
                                                resultBuilder);
            }
        }
Ejemplo n.º 6
0
 private static void HandleSpecialValues(
     ref GrisuDouble double_inspect,
     TextWriter resultBuilder)
 {
     if (double_inspect.IsInfinite)
     {
         if (double_inspect.Value < 0)
         {
             resultBuilder.Write('-');
         }
         resultBuilder.Write(infinity_symbol_);
         return;
     }
     if (double_inspect.IsNaN)
     {
         resultBuilder.Write(nan_symbol_);
         return;
     }
 }
Ejemplo n.º 7
0
        // Provides a decimal representation of v.
        // Returns true if it succeeds, otherwise the result cannot be trusted.
        // There will be *length digits inside the buffer (not null-terminated).
        // If the function returns true then
        //        v == (double) (buffer * 10^decimal_exponent).
        // The digits in the buffer are the shortest representation possible: no
        // 0.09999999999999999 instead of 0.1. The shorter representation will even be
        // chosen even if the longer one would be closer to v.
        // The last digit will be closest to the actual v. That is, even if several
        // digits might correctly yield 'v' when read again, the closest will be
        // computed.
        private static bool Grisu3(ref GrisuDouble v,
                                   char[] buffer,
                                   out int length,
                                   out int decimal_exponent)
        {
            DiyFp w = v.AsNormalizedDiyFp();
            // boundary_minus and boundary_plus are the boundaries between v and its
            // closest floating-point neighbors. Any number strictly between
            // boundary_minus and boundary_plus will round to v when convert to a double.
            // Grisu3 will never output representations that lie exactly on a boundary.
            DiyFp boundary_minus, boundary_plus;

            v.NormalizedBoundaries(out boundary_minus, out boundary_plus);
            Debug.Assert(boundary_plus.E == w.E);
            DiyFp ten_mk;  // Cached power of ten: 10^-k
            int   mk;      // -k
            int   ten_mk_minimal_binary_exponent =
                kMinimalTargetExponent - (w.E + DiyFp.kSignificandSize);
            int ten_mk_maximal_binary_exponent =
                kMaximalTargetExponent - (w.E + DiyFp.kSignificandSize);

            PowersOfTenCache.GetCachedPowerForBinaryExponentRange(
                ten_mk_minimal_binary_exponent,
                ten_mk_maximal_binary_exponent,
                out ten_mk, out mk);
            Debug.Assert((kMinimalTargetExponent <= w.E + ten_mk.E +
                          DiyFp.kSignificandSize) &&
                         (kMaximalTargetExponent >= w.E + ten_mk.E +
                          DiyFp.kSignificandSize));
            // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
            // 64 bit significand and ten_mk is thus only precise up to 64 bits.

            // The DiyFp.Times procedure rounds its result, and ten_mk is approximated
            // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
            // off by a small amount.
            // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
            // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
            //           (f-1) * 2^e < w*10^k < (f+1) * 2^e
            //DiyFp scaled_w = DiyFp.Times(ref w, ref ten_mk);
            w.Multiply(ref ten_mk);
            Debug.Assert(w.E ==
                         boundary_plus.E + ten_mk.E + DiyFp.kSignificandSize);
            // In theory it would be possible to avoid some recomputations by computing
            // the difference between w and boundary_minus/plus (a power of 2) and to
            // compute scaled_boundary_minus/plus by subtracting/adding from
            // scaled_w. However the code becomes much less readable and the speed
            // enhancements are not terriffic.
            //DiyFp scaled_boundary_minus = DiyFp.Times(ref boundary_minus, ref ten_mk);
            boundary_minus.Multiply(ref ten_mk);
            //DiyFp scaled_boundary_plus = DiyFp.Times(ref boundary_plus, ref ten_mk);
            boundary_plus.Multiply(ref ten_mk);

            // DigitGen will generate the digits of scaled_w. Therefore we have
            // v == (double) (scaled_w * 10^-mk).
            // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
            // integer than it will be updated. For instance if scaled_w == 1.23 then
            // the buffer will be filled with "123" und the decimal_exponent will be
            // decreased by 2.
            int  kappa;
            bool result = DigitGen(ref boundary_minus, ref w, ref boundary_plus,
                                   buffer, out length, out kappa);

            decimal_exponent = -mk + kappa;
            return(result);
        }
Ejemplo n.º 8
0
        public static void DoubleToString(double value, TextWriter resultBuilder)
        {
            if (value < 0.0)
            {
                resultBuilder.Write('-');
                value = -value;
            }

            GrisuDouble grisuDouble = new GrisuDouble(value);

            if (grisuDouble.IsSpecial)
            {
                HandleSpecialValues(ref grisuDouble, resultBuilder);
                return;
            }

            char[] decimal_rep = ts_decimal_rep;
            if (decimal_rep == null)
            {
                decimal_rep = ts_decimal_rep = new char[kBase10MaximalLength + 1];
            }

            int decimal_point;
            int decimal_rep_length;

            if (!DoubleToShortestAscii(ref grisuDouble, decimal_rep, out decimal_rep_length, out decimal_point))
            {
                resultBuilder.Write("{0:R}", value);
                return;
            }

            int decimalRepLength = decimal_rep_length + 1;

            if (decimal_point < 1)
            {
                decimalRepLength += -decimal_point + 1;
            }
            else if (decimal_point >= decimal_rep_length)
            {
                decimalRepLength += decimal_point - decimal_rep_length + 1;
            }

            int exponent          = decimal_point - 1;
            int absExponent       = Math.Abs(exponent);
            int exponentRepLength = decimal_rep_length + 3;

            if (exponent < 0)
            {
                ++exponentRepLength;
            }
            if (absExponent >= 10)
            {
                ++exponentRepLength;
                if (absExponent >= 100)
                {
                    ++exponentRepLength;
                }
            }

            if (decimalRepLength <= exponentRepLength)
            {
                CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
                                            decimal_point,
                                            Math.Max(0, decimal_rep_length - decimal_point),
                                            resultBuilder);
            }
            else
            {
                CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
                                                resultBuilder);
            }
        }