コード例 #1
0
        private static bool FastDtoa(double v,
                                     FastDtoaMode mode,
                                     Span <byte> buffer,
                                     out int length,
                                     out int decimalPoint)
        {
            switch (mode)
            {
            case FastDtoaMode.FastDtoaShortest:
            case FastDtoaMode.FastDtoaShortestSingle:
                var result = Grisu3(v, mode, buffer, out length, out var decimalExponent);
                decimalPoint = result ? length + decimalExponent : -1;
                return(result);

            // case FastDtoaMode.FAST_DTOA_PRECISION:
            //result = Grisu3Counted(v, requested_digits, buffer, length, &decimal_exponent);
            default:
                throw new Exception("unreachable code.");
            }
        }
コード例 #2
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(double v, FastDtoaMode mode, Span <byte> buffer, out int length, out int decimalExponent)
        {
            var w = new IeeeDouble(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 boundaryMinus, boundaryPlus;

            switch (mode)
            {
            case FastDtoaMode.FastDtoaShortest:
                new IeeeDouble(v).NormalizedBoundaries(out boundaryMinus, out boundaryPlus);
                break;

            case FastDtoaMode.FastDtoaShortestSingle:
            {
                var singleV = (float)v;
                new IeeeSingle(singleV).NormalizedBoundaries(out boundaryMinus, out boundaryPlus);
                break;
            }

            default:
                throw new Exception("Invalid Mode.");
            }

            var tenMkMinimalBinaryExponent = KMinimalTargetExponent - (w.e + DiyFp.kSignificandSize);

            PowersOfTenCache.GetCachedPowerForBinaryExponentRange(tenMkMinimalBinaryExponent, out var tenMk, out var mk);

            // 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
            var scaledW = DiyFp.Times(ref w, ref tenMk);

            // 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 terrific.
            var scaledBoundaryMinus = DiyFp.Times(ref boundaryMinus, ref tenMk);
            var scaledBoundaryPlus  = DiyFp.Times(ref boundaryPlus, ref tenMk);

            // 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.
            var result = DigitGen(scaledBoundaryMinus, scaledW, scaledBoundaryPlus,
                                  buffer, out length, out var kappa);

            decimalExponent = -mk + kappa;
            return(result);
        }