Esempio n. 1
0
        // this = this * other.
        public void Multiply(ref DiyFp other)
        {
            // Simply "emulates" a 128 bit multiplication.
            // However: the resulting number only contains 64 bits. The least
            // significant 64 bits are only used for rounding the most significant 64
            // bits.
            const long kM32 = 0xFFFFFFFFU;
            ulong      a    = f >> 32;
            ulong      b    = f & kM32;
            ulong      c    = other.f >> 32;
            ulong      d    = other.f & kM32;
            ulong      ac   = a * c;
            ulong      bc   = b * c;
            ulong      ad   = a * d;
            ulong      bd   = b * d;
            ulong      tmp  = (bd >> 32) + (ad & kM32) + (bc & kM32);

            // By adding 1U << 31 to tmp we round the final result.
            // Halfway cases will be round up.
            tmp += 1U << 31;
            ulong result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);

            e += other.e + 64;
            f  = result_f;
        }
Esempio n. 2
0
            public static bool TryRunDouble(double value, int requestedDigits, ref NumberBuffer number)
            {
                var v = value.IsNegative() ? -value : value;

                Debug.Assert(v > 0);
                Debug.Assert(v.IsFinite());

                int  length;
                int  decimalExponent;
                bool result;

                if (requestedDigits == -1)
                {
                    var w = DiyFp.CreateAndGetBoundaries(v, out var boundaryMinus, out var boundaryPlus).Normalize();
                    result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.DigitsMut, out length, out decimalExponent);
                }
                else
                {
                    var w = new DiyFp(v).Normalize();
                    result = TryRunCounted(in w, requestedDigits, number.DigitsMut, out length, out decimalExponent);
                }

                if (!result)
                {
                    return(result);
                }
                Debug.Assert(requestedDigits == -1 || length == requestedDigits);

                number.Scale             = length + decimalExponent;
                number.DigitsMut[length] = (byte)'\0';
                number.DigitsCount       = length;

                return(result);
            }
Esempio n. 3
0
        // Returns the two boundaries of first argument.
        // The bigger boundary (m_plus) is normalized. The lower boundary has the same
        // exponent as m_plus.
        internal static void NormalizedBoundaries(long d64, DiyFp mMinus, DiyFp mPlus)
        {
            DiyFp v = AsDiyFp(d64);
            bool  significandIsZero = (v.F == KHiddenBit);

            mPlus.F = (v.F << 1) + 1;
            mPlus.E = v.E - 1;
            mPlus.Normalize();
            if (significandIsZero && v.E != KDenormalExponent)
            {
                // The boundary is closer. Think of v = 1000e10 and v- = 9999e9.
                // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
                // at a distance of 1e8.
                // The only exception is for the smallest normal: the largest denormal is
                // at the same distance as its successor.
                // Note: denormals have the same exponent as the smallest normals.
                mMinus.F = (v.F << 2) - 1;
                mMinus.E = v.E - 2;
            }
            else
            {
                mMinus.F = (v.F << 1) - 1;
                mMinus.E = v.E - 1;
            }
            mMinus.F = mMinus.F << (mMinus.E - mPlus.E);
            mMinus.E = mPlus.E;
        }
Esempio n. 4
0
            private static void CachedPower(int k, out DiyFp cmk, out int decimalExponent)
            {
                int index = ((PowerOffset + k - 1) / PowerDecimalExponentDistance) + 1;

                cmk             = new DiyFp(s_CachedPowerSignificands[index], s_CachedPowerBinaryExponents[index]);
                decimalExponent = s_CachedPowerDecimalExponents[index];
            }
Esempio n. 5
0
            public static bool TryRunSingle(float value, int requestedDigits, ref NumberBuffer number)
            {
                float v = float.IsNegative(value) ? -value : value;

                Debug.Assert(v > 0);
                Debug.Assert(float.IsFinite(v));

                int  length;
                int  decimalExponent;
                bool result;

                if (requestedDigits == -1)
                {
                    DiyFp w = DiyFp.CreateAndGetBoundaries(v, out DiyFp boundaryMinus, out DiyFp boundaryPlus).Normalize();
                    result = TryRunShortest(in boundaryMinus, in w, in boundaryPlus, number.Digits, out length, out decimalExponent);
                }
                else
                {
                    DiyFp w = new DiyFp(v).Normalize();
                    result = TryRunCounted(in w, requestedDigits, number.Digits, out length, out decimalExponent);
                }

                if (result)
                {
                    Debug.Assert((requestedDigits == -1) || (length == requestedDigits));

                    number.Scale          = length + decimalExponent;
                    number.Digits[length] = (byte)('\0');
                    number.DigitsCount    = length;
                }

                return(result);
            }
Esempio n. 6
0
        // Returns a - b.
        // The exponents of both numbers must be the same and this must be bigger
        // than other. The result will not be normalized.
        public static DiyFp Minus(ref DiyFp a, ref DiyFp b)
        {
            DiyFp result = a;

            result.Subtract(ref b);
            return(result);
        }
        // this = this * other.
        public void Multiply(ref DiyFp other)
        {
            // Simply "emulates" a 128 bit multiplication.
            // However: the resulting number only contains 64 bits. The least
            // significant 64 bits are only used for rounding the most significant 64
            // bits.
            const long kM32 = 0xFFFFFFFFU;
            var        a    = F >> 32;
            var        b    = F & kM32;
            var        c    = other.F >> 32;
            var        d    = other.F & kM32;
            var        ac   = a * c;
            var        bc   = b * c;
            var        ad   = a * d;
            var        bd   = b * d;
            var        tmp  = (bd >> 32) + (ad & kM32) + (bc & kM32);

            // By adding 1U << 31 to tmp we round the final result.
            // Halfway cases will be round up.
            tmp += 1U << 31;
            var result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);

            E += other.E + 64;
            F  = result_f;
        }
Esempio n. 8
0
        // returns a * b;
        public static DiyFp Times(ref DiyFp a, ref DiyFp b)
        {
            DiyFp result = a;

            result.Multiply(ref b);
            return(result);
        }
Esempio n. 9
0
            public void Minus(ref DiyFp rhs)
            {
                Debug.Assert(_e == rhs._e);
                Debug.Assert(_f >= rhs._f);

                _f -= rhs._f;
            }
Esempio n. 10
0
        // this = this * other.
        private void Multiply(DiyFp other)
        {
            // Simply "emulates" a 128 bit multiplication.
            // However: the resulting number only contains 64 bits. The least
            // significant 64 bits are only used for rounding the most significant 64
            // bits.
            const long kM32 = 0xFFFFFFFFL;
            long       a    = F.UnsignedShift(32);
            long       b    = F & kM32;
            long       c    = other.F.UnsignedShift(32);
            long       d    = other.F & kM32;
            long       ac   = a * c;
            long       bc   = b * c;
            long       ad   = a * d;
            long       bd   = b * d;
            long       tmp  = bd.UnsignedShift(32) + (ad & kM32) + (bc & kM32);

            // By adding 1U << 31 to tmp we round the final result.
            // Halfway cases will be round up.
            tmp += 1L << 31;
            long resultF = ac + ad.UnsignedShift(32) + bc.UnsignedShift(32) + tmp.UnsignedShift(32);

            E += other.E + 64;
            F  = resultF;
        }
Esempio n. 11
0
        // Reads a DiyFp from the buffer.
        // The returned DiyFp is not necessarily normalized.
        // If remaining_decimals is zero then the returned DiyFp is accurate.
        // Otherwise it has been rounded and has error of at most 1/2 ulp.
        private static void ReadDiyFp(Vector buffer,
                                      out DiyFp result,
                                      out int remainingDecimals
                                      )
        {
            var significand = ReadUint64(buffer, out var readDigits);

            if (buffer.length() == readDigits)
            {
                result            = new DiyFp(significand, 0);
                remainingDecimals = 0;
            }
            else
            {
                // Round the significand.
                if (buffer[readDigits] >= '5')
                {
                    significand++;
                }
                // Compute the binary exponent.
                var exponent = 0;
                result            = new DiyFp(significand, exponent);
                remainingDecimals = buffer.length() - readDigits;
            }
        }
Esempio n. 12
0
        // Returns a - b.
        // The exponents of both numbers must be the same and this must be bigger
        // than other. The result will not be normalized.
        internal static DiyFp Minus(DiyFp a, DiyFp b)
        {
            DiyFp result = new DiyFp(a.F, a.E);

            result.Subtract(b);
            return(result);
        }
Esempio n. 13
0
        // returns a * b;
        internal static DiyFp Times(DiyFp a, DiyFp b)
        {
            DiyFp result = new DiyFp(a.F, a.E);

            result.Multiply(b);
            return(result);
        }
Esempio n. 14
0
        public static DiyFp Normalize(ref DiyFp a)
        {
            DiyFp result = a;

            result.Normalize();
            return(result);
        }
Esempio n. 15
0
            // Computes the two boundaries of value.
            //
            // The bigger boundary (mPlus) is normalized.
            // The lower boundary has the same exponent as mPlus.
            //
            // Precondition:
            //  The value encoded by value must be greater than 0.
            public static DiyFp CreateAndGetBoundaries(float value, out DiyFp mMinus, out DiyFp mPlus)
            {
                var result = new DiyFp(value);

                result.GetBoundaries(SingleImplicitBitIndex, out mMinus, out mPlus);
                return(result);
            }
Esempio n. 16
0
            public void NormalizedBoundaries(out DiyFp minus, out DiyFp plus)
            {
                var pl = new DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
                var mi = f == kDpHiddenBit ? new DiyFp((f << 2) - 1, e - 2) : new DiyFp((f << 1) - 1, e - 1);

                mi.f <<= mi.e - pl.e;
                mi.e   = pl.e;
                plus   = pl;
                minus  = mi;
            }
Esempio n. 17
0
        private static unsafe int DigitGen(DiyFp W, DiyFp Mp, ulong delta, char *buffer, ref int K)
        {
            var one    = new DiyFp((ulong)1 << -Mp.e, Mp.e);
            var wp_w   = Mp - W;
            var p1     = (uint)(Mp.f >> -one.e);
            var p2     = Mp.f & (one.f - 1);
            var kappa  = log10(p1) + 1;
            var length = 0;

            var divisor = kPow10[kappa - 1];

            while (kappa > 0)
            {
                var d = p1 / divisor;
                p1 %= divisor;
                if (d != 0 || length != 0)
                {
                    *buffer++ = (char)('0' + d);
                    length++;
                }
                kappa--;
                var tmp = ((ulong)p1 << -one.e) + p2;
                if (tmp <= delta)
                {
                    K += kappa;
                    GrisuRound(buffer, delta, tmp, (ulong)divisor << -one.e, wp_w.f);
                    return(length);
                }
                divisor /= 10;
            }

            var pow10 = 10ul;

            // kappa = 0
            for (;;)
            {
                p2    *= 10;
                delta *= 10;
                var d1 = (char)(p2 >> -one.e);
                if (d1 != 0 || length != 0)
                {
                    *buffer++ = (char)('0' + d1);
                    length++;
                }
                p2 &= one.f - 1;
                kappa--;
                if (p2 < delta)
                {
                    K += kappa;
                    GrisuRound(buffer, delta, p2, one.f, wp_w.f * pow10);
                    return(length);
                }
                pow10 *= 10;
            }
        }
Esempio n. 18
0
        // Computes the two boundaries of this.
        // The bigger boundary (m_plus) is normalized. The lower boundary has the same
        // exponent as m_plus.
        // Precondition: the value encoded by this Single must be greater than 0.
        public void NormalizedBoundaries(out DiyFp outMMinus, out DiyFp outMPlus)
        {
            var v      = this.AsDiyFp();
            var __     = new DiyFp((v.f << 1) + 1, v.e - 1);
            var mPlus  = DiyFp.Normalize(ref __);
            var mMinus = LowerBoundaryIsCloser() ? new DiyFp((v.f << 2) - 1, v.e - 2) : new DiyFp((v.f << 1) - 1, v.e - 1);

            mMinus.f <<= mMinus.e - mPlus.e;
            mMinus.e   = mPlus.e;
            outMPlus   = mPlus;
            outMMinus  = mMinus;
        }
Esempio n. 19
0
        internal static int GetCachedPower(int e, int alpha, int gamma, DiyFp cMk)
        {
            const int   kQ          = DiyFp.KSignificandSize;
            double      k           = System.Math.Ceiling((alpha - e + kQ - 1) * Kd1Log210);
            int         index       = (GrisuCacheOffset + (int)k - 1) / CachedPowersSpacing + 1;
            CachedPower cachedPower = CACHED_POWERS[index];

            cMk.F = cachedPower.Significand;
            cMk.E = cachedPower.BinaryExponent;

            return(cachedPower.DecimalExponent);
        }
Esempio n. 20
0
        private static unsafe int Grisu2(double value, char *buffer, out int K)
        {
            var v = new DiyFp(value);

            v.NormalizedBoundaries(out var w_m, out var w_p);

            var c_mk = GetCachedPower(w_p.e, out K);
            var W    = v.Normalize() * c_mk;
            var Wp   = w_p * c_mk;
            var Wm   = w_m * c_mk;

            Wm.f++;
            Wp.f--;
            return(DigitGen(W, Wp, Wp.f - Wm.f, buffer, ref K));
        }
Esempio n. 21
0
        private static unsafe int Grisu2(double value, char *buffer, out int K)
        {
            DiyFp diyFp = new DiyFp(value);
            DiyFp minus;
            DiyFp plus;

            diyFp.NormalizedBoundaries(out minus, out plus);
            DiyFp cachedPower = GetCachedPower(plus.e, out K);
            DiyFp w           = diyFp.Normalize() * cachedPower;
            DiyFp mp          = plus * cachedPower;
            DiyFp diyFp2      = minus * cachedPower;

            diyFp2.f++;
            mp.f--;
            return(DigitGen(w, mp, mp.f - diyFp2.f, buffer, ref K));
        }
Esempio n. 22
0
            public static void GenerateNormalizedDiyFp(double value, out DiyFp result)
            {
                Debug.Assert(value > 0.0);

                long f = ExtractFractionAndBiasedExponent(value, out int e);

                while ((f & (1L << 52)) == 0)
                {
                    f <<= 1;
                    e--;
                }

                int lengthDelta = (SignificandLength - 53);

                f <<= lengthDelta;
                e  -= lengthDelta;

                result = new DiyFp((ulong)(f), e);
            }
Esempio n. 23
0
        public static ulong DiyFpToUint64(DiyFp diyFp)
        {
            var significand = diyFp.f;
            var exponent    = diyFp.e;

            while (significand > KHiddenBit + KSignificandMask)
            {
                significand >>= 1;
                exponent++;
            }

            if (exponent >= KMaxExponent)
            {
                return(KInfinity);
            }

            if (exponent < KDenormalExponent)
            {
                return(0);
            }

            while (exponent > KDenormalExponent && (significand & KHiddenBit) == 0)
            {
                significand <<= 1;
                exponent--;
            }

            ulong biasedExponent;

            if (exponent == KDenormalExponent && (significand & KHiddenBit) == 0)
            {
                biasedExponent = 0;
            }
            else
            {
                biasedExponent = (ulong)(exponent + KExponentBias);
            }

            return((significand & KSignificandMask) | (biasedExponent << KPhysicalSignificandSize));
        }
Esempio n. 24
0
            public void Multiply(ref DiyFp rhs)
            {
                ulong lf = _f;
                ulong rf = rhs._f;

                uint a = (uint)(lf >> 32);
                uint b = (uint)(lf);

                uint c = (uint)(rf >> 32);
                uint d = (uint)(rf);

                ulong ac = ((ulong)(a) * c);
                ulong bc = ((ulong)(b) * c);
                ulong ad = ((ulong)(a) * d);
                ulong bd = ((ulong)(b) * d);

                ulong tmp = (bd >> 32) + (uint)(ad) + (uint)(bc);

                tmp += (1UL << 31);

                _f  = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
                _e += (rhs._e + SignificandLength);
            }
Esempio n. 25
0
        // If the function returns true then the result is the correct double.
        // Otherwise it is either the correct double or the double that is just below
        // the correct double.
        private static bool DiyFpStrtod(Vector buffer,
                                        int exponent,
                                        out double result
                                        )
        {
            ReadDiyFp(buffer, out var input, out var remainingDecimals);
            // Since we may have dropped some digits the input is not accurate.
            // If remaining_decimals is different than 0 than the error is at most
            // .5 ulp (unit in the last place).
            // We don't want to deal with fractions and therefore keep a common
            // denominator.
            const int kDenominatorLog = 3;
            const int kDenominator    = 1 << kDenominatorLog;

            // Move the remaining decimals into the exponent.
            exponent += remainingDecimals;
            var error = (ulong)(remainingDecimals == 0 ? 0 : kDenominator / 2);

            var oldE = input.E;

            input.Normalize();
            error <<= oldE - input.E;

            if (exponent < PowersOfTenCache.KMinDecimalExponent)
            {
                result = 0.0;
                return(true);
            }

            PowersOfTenCache.GetCachedPowerForDecimalExponent(exponent,
                                                              out var cachedPower,
                                                              out var cachedDecimalExponent);

            if (cachedDecimalExponent != exponent)
            {
                var adjustmentExponent = exponent - cachedDecimalExponent;
                var adjustmentPower    = AdjustmentPowerOfTen(adjustmentExponent);
                input.Multiply(ref adjustmentPower);
                if (KMaxUint64DecimalDigits - buffer.length() >= adjustmentExponent)
                {
                    // The product of input with the adjustment power fits into a 64 bit
                    // integer.
                }
                else
                {
                    // The adjustment power is exact. There is hence only an error of 0.5.
                    error += kDenominator / 2;
                }
            }

            input.Multiply(ref cachedPower);
            // The error introduced by a multiplication of a*b equals
            //   error_a + error_b + error_a*error_b/2^64 + 0.5
            // Substituting a with 'input' and b with 'cached_power' we have
            //   error_b = 0.5  (all cached powers have an error of less than 0.5 ulp),
            //   error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
            var errorB     = kDenominator / 2;
            var errorAb    = (error == 0 ? 0 : 1);          // We round up to 1.
            var fixedError = kDenominator / 2;

            error += (ulong)(errorB + errorAb + fixedError);

            oldE = input.E;
            input.Normalize();
            error <<= oldE - input.E;

            // See if the double's significand changes if we add/subtract the error.
            var orderOfMagnitude         = DiyFp.KSignificandSize + input.E;
            var effectiveSignificandSize = Double.SignificandSizeForOrderOfMagnitude(orderOfMagnitude);
            var precisionDigitsCount     = DiyFp.KSignificandSize - effectiveSignificandSize;

            if (precisionDigitsCount + kDenominatorLog >= DiyFp.KSignificandSize)
            {
                // This can only happen for very small denormals. In this case the
                // half-way multiplied by the denominator exceeds the range of an uint64.
                // Simply shift everything to the right.
                var shiftAmount = (precisionDigitsCount + kDenominatorLog) -
                                  DiyFp.KSignificandSize + 1;
                input.F = (input.F >> shiftAmount);
                input.E = (input.E + shiftAmount);
                // We add 1 for the lost precision of error, and kDenominator for
                // the lost precision of input.f().
                error = (error >> shiftAmount) + 1 + kDenominator;
                precisionDigitsCount -= shiftAmount;
            }
            // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
            ulong one64             = 1;
            var   precisionBitsMask = (one64 << precisionDigitsCount) - 1;
            var   precisionBits     = input.F & precisionBitsMask;
            var   halfWay           = one64 << (precisionDigitsCount - 1);

            precisionBits *= kDenominator;
            halfWay       *= kDenominator;
            var roundedInput = new DiyFp(input.F >> precisionDigitsCount, input.E + precisionDigitsCount);

            if (precisionBits >= halfWay + error)
            {
                roundedInput.F = (roundedInput.F + 1);
            }
            // If the last_bits are too close to the half-way case than we are too
            // inaccurate and round down. In this case we return false so that we can
            // fall back to a more precise algorithm.

            result = new Double(roundedInput).value();
            if (halfWay - error < precisionBits && precisionBits < halfWay + error)
            {
                // Too imprecise. The caller will have to fall back to a slower version.
                // However the returned number is guaranteed to be either the correct
                // double, or the next-lower double.
                return(false);
            }

            return(true);
        }
Esempio n. 26
0
 public IeeeDouble(DiyFp d)
 {
     d64 = DiyFpToUint64(d);
 }
Esempio n. 27
0
            private static bool DigitGen(ref DiyFp mp, int precision, byte *digits, out int length, out int k)
            {
                // Split the input mp to two parts. Part 1 is integral. Part 2 can be used to calculate
                // fractional.
                //
                // mp: the input DiyFp scaled by cached power.
                // K: final kappa.
                // p1: part 1.
                // p2: part 2.

                Debug.Assert(precision > 0);
                Debug.Assert(digits != null);
                Debug.Assert(mp.e >= Alpha);
                Debug.Assert(mp.e <= Gamma);

                ulong mpF = mp.f;
                int   mpE = mp.e;

                var one = new DiyFp(1UL << -mpE, mpE);

                ulong oneF    = one.f;
                int   oneNegE = -one.e;

                ulong ulp = 1;

                uint  p1 = (uint)(mpF >> oneNegE);
                ulong p2 = mpF & (oneF - 1);

                // When p2 (fractional part) is zero, we can predicate if p1 is good to produce the numbers in requested digit count:
                //
                // - When requested digit count >= 11, p1 is not be able to exhaust the count as 10^(11 - 1) > uint.MaxValue >= p1.
                // - When p1 < 10^(count - 1), p1 is not be able to exhaust the count.
                // - Otherwise, p1 may have chance to exhaust the count.
                if ((p2 == 0) && ((precision >= 11) || (p1 < s_CachedPowerOfTen[precision - 1])))
                {
                    length = 0;
                    k      = 0;
                    return(false);
                }

                // Note: The code in the paper simply assigns div to Ten9 and kappa to 10 directly.
                // That means we need to check if any leading zero of the generated
                // digits during the while loop, which hurts the performance.
                //
                // Now if we can estimate what the div and kappa, we do not need to check the leading zeros.
                // The idea is to find the biggest power of 10 that is less than or equal to the given number.
                // Then we don't need to worry about the leading zeros and we can get 10% performance gain.
                int index = 0;

                BiggestPowerTenLessThanOrEqualTo(p1, (DiyFp.SignificandLength - oneNegE), out uint div, out int kappa);
                kappa++;

                // Produce integral.
                while (kappa > 0)
                {
                    int d = (int)(Math.DivRem(p1, div, out p1));
                    digits[index] = (byte)('0' + d);

                    index++;
                    precision--;
                    kappa--;

                    if (precision == 0)
                    {
                        break;
                    }

                    div /= 10;
                }

                // End up here if we already exhausted the digit count.
                if (precision == 0)
                {
                    ulong rest = ((ulong)(p1) << oneNegE) + p2;

                    length = index;
                    k      = kappa;

                    return(RoundWeed(
                               digits,
                               index,
                               rest,
                               ((ulong)(div)) << oneNegE,
                                   ulp,
                                   ref k
                               ));
                }

                // We have to generate digits from part2 if we have requested digit count left
                // and part2 is greater than ulp.
                while ((precision > 0) && (p2 > ulp))
                {
                    p2 *= 10;

                    int d = (int)(p2 >> oneNegE);
                    digits[index] = (byte)('0' + d);

                    index++;
                    precision--;
                    kappa--;

                    p2 &= (oneF - 1);

                    ulp *= 10;
                }

                // If we haven't exhausted the requested digit counts, the Grisu3 algorithm fails.
                if (precision != 0)
                {
                    length = 0;
                    k      = 0;
                    return(false);
                }

                length = index;
                k      = kappa;

                return(RoundWeed(digits, index, p2, oneF, ulp, ref k));
            }
 // this = this - other.
 // The exponents of both numbers must be the same and the significand of this
 // must be bigger than the significand of other.
 // The result will not be normalized.
 public void Subtract(ref DiyFp other) => F -= other.F;
Esempio n. 29
0
            public static bool Run(double value, int precision, ref NumberBuffer number)
            {
                // ========================================================================================================================================
                // This implementation is based on the paper: http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
                // You must read this paper to fully understand the code.
                //
                // Deviation: Instead of generating shortest digits, we generate the digits according to the input count.
                // Therefore, we do not need m+ and m- which are used to determine the exact range of values.
                // ========================================================================================================================================
                //
                // Overview:
                //
                // The idea of Grisu3 is to leverage additional bits and cached power of ten to produce the digits.
                // We need to create a handmade floating point data structure DiyFp to extend the bits of double.
                // We also need to cache the powers of ten for digits generation. By choosing the correct index of powers
                // we need to start with, we can eliminate the expensive big num divide operation.
                //
                // Grisu3 is imprecision for some numbers. Fortunately, the algorithm itself can determine that and give us
                // a success/fail flag. We may fall back to other algorithms (For instance, Dragon4) if it fails.
                //
                // w: the normalized DiyFp from the input value.
                // mk: The index of the cached powers.
                // cmk: The cached power.
                // D: Product: w * cmk.
                // kappa: A factor used for generating digits. See step 5 of the Grisu3 procedure in the paper.

                // Handle sign bit.
                if (double.IsNegative(value))
                {
                    value             = -value;
                    number.IsNegative = true;
                }
                else
                {
                    number.IsNegative = false;
                }

                // Step 1: Determine the normalized DiyFp w.

                DiyFp.GenerateNormalizedDiyFp(value, out DiyFp w);

                // Step 2: Find the cached power of ten.

                // Compute the proper index mk.
                int mk = KComp(w.e + DiyFp.SignificandLength);

                // Retrieve the cached power of ten.
                CachedPower(mk, out DiyFp cmk, out int decimalExponent);

                // Step 3: Scale the w with the cached power of ten.

                DiyFp.Multiply(ref w, ref cmk, out DiyFp D);

                // Step 4: Generate digits.

                bool isSuccess = DigitGen(ref D, precision, number.GetDigitsPointer(), out int length, out int kappa);

                if (isSuccess)
                {
                    number.Digits[precision] = (byte)('\0');
                    number.Scale             = (length - decimalExponent + kappa);
                }

                return(isSuccess);
            }
Esempio n. 30
0
 // this = this - other.
 // The exponents of both numbers must be the same and the significand of this
 // must be bigger than the significand of other.
 // The result will not be normalized.
 public void Subtract(ref DiyFp other)
 {
     f -= other.f;
 }