예제 #1
0
파일: DiyFp.cs 프로젝트: san45/czml-writer
        // returns a * b;
        public static DiyFp Times(ref DiyFp a, ref DiyFp b)
        {
            DiyFp result = a;

            result.Multiply(ref b);
            return(result);
        }
예제 #2
0
파일: DiyFp.cs 프로젝트: san45/czml-writer
        // 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 ulong 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;
        }
예제 #3
0
파일: DiyFp.cs 프로젝트: san45/czml-writer
        // 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);
        }
예제 #4
0
파일: DiyFp.cs 프로젝트: san45/czml-writer
        public static DiyFp Normalize(ref DiyFp a)
        {
            DiyFp result = a;

            result.Normalize();
            return(result);
        }
예제 #5
0
 // Returns a cached power of ten x ~= 10^k such that
 //   k <= decimal_exponent < k + kCachedPowersDecimalDistance.
 // The given decimal_exponent must satisfy
 //   kMinDecimalExponent <= requested_exponent, and
 //   requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
 public static void GetCachedPowerForDecimalExponent(int requested_exponent,
                                              out DiyFp power,
                                              out int found_exponent)
 {
     Debug.Assert(kMinDecimalExponent <= requested_exponent);
     Debug.Assert(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
     int index =
         (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
     CachedPower cached_power = kCachedPowers[index];
     power = new DiyFp(cached_power.significand, cached_power.binary_exponent);
     found_exponent = cached_power.decimal_exponent;
     Debug.Assert(found_exponent <= requested_exponent);
     Debug.Assert(requested_exponent < found_exponent + kDecimalExponentDistance);
 }
예제 #6
0
        // Returns a cached power of ten x ~= 10^k such that
        //   k <= decimal_exponent < k + kCachedPowersDecimalDistance.
        // The given decimal_exponent must satisfy
        //   kMinDecimalExponent <= requested_exponent, and
        //   requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
        public static void GetCachedPowerForDecimalExponent(int requested_exponent,
                                                            out DiyFp power,
                                                            out int found_exponent)
        {
            Debug.Assert(kMinDecimalExponent <= requested_exponent);
            Debug.Assert(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
            int index =
                (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
            CachedPower cached_power = kCachedPowers[index];

            power          = new DiyFp(cached_power.significand, cached_power.binary_exponent);
            found_exponent = cached_power.decimal_exponent;
            Debug.Assert(found_exponent <= requested_exponent);
            Debug.Assert(requested_exponent < found_exponent + kDecimalExponentDistance);
        }
예제 #7
0
 // Returns a cached power-of-ten with a binary exponent in the range
 // [min_exponent; max_exponent] (boundaries included).
 public static void GetCachedPowerForBinaryExponentRange(int min_exponent,
                                                  int max_exponent,
                                                  out DiyFp power,
                                                  out int decimal_exponent)
 {
     int kQ = DiyFp.kSignificandSize;
     double k = Math.Ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
     int foo = kCachedPowersOffset;
     int index =
         (foo + (int)(k) - 1) / kDecimalExponentDistance + 1;
     Debug.Assert(0 <= index && index < kCachedPowersLength);
     CachedPower cached_power = kCachedPowers[index];
     Debug.Assert(min_exponent <= cached_power.binary_exponent);
     Debug.Assert(cached_power.binary_exponent <= max_exponent);
     decimal_exponent = cached_power.decimal_exponent;
     power = new DiyFp(cached_power.significand, cached_power.binary_exponent);
 }
예제 #8
0
        // Returns a cached power-of-ten with a binary exponent in the range
        // [min_exponent; max_exponent] (boundaries included).
        public static void GetCachedPowerForBinaryExponentRange(int min_exponent,
                                                                int max_exponent,
                                                                out DiyFp power,
                                                                out int decimal_exponent)
        {
            int    kQ    = DiyFp.kSignificandSize;
            double k     = Math.Ceiling((min_exponent + kQ - 1) * kD_1_LOG2_10);
            int    foo   = kCachedPowersOffset;
            int    index =
                (foo + (int)(k) - 1) / kDecimalExponentDistance + 1;

            Debug.Assert(0 <= index && index < kCachedPowersLength);
            CachedPower cached_power = kCachedPowers[index];

            Debug.Assert(min_exponent <= cached_power.binary_exponent);
            Debug.Assert(cached_power.binary_exponent <= max_exponent);
            decimal_exponent = cached_power.decimal_exponent;
            power            = new DiyFp(cached_power.significand, cached_power.binary_exponent);
        }
예제 #9
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 ulong 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;
 }
예제 #10
0
        private static ulong DiyFpToUInt64(DiyFp diy_fp)
        {
            ulong significand = diy_fp.F;
            int   exponent    = diy_fp.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 biased_exponent;

            if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0)
            {
                biased_exponent = 0;
            }
            else
            {
                biased_exponent = (ulong)(exponent + kExponentBias);
            }
            return((significand & kSignificandMask) |
                   (biased_exponent << kPhysicalSignificandSize));
        }
예제 #11
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 Double must be greater than 0.
        public void NormalizedBoundaries(out DiyFp out_m_minus, out DiyFp out_m_plus)
        {
            Debug.Assert(Value > 0.0);

            ulong d64 = d64_;
            ulong vF;
            int   vE;

            if (IsDenormal)
            {
                vF = d64 & kSignificandMask;
                vE = kDenormalExponent;
            }
            else
            {
                vF = (d64 & kSignificandMask) + kHiddenBit;
                vE = (int)((d64 & kExponentMask) >> kPhysicalSignificandSize) - kExponentBias;
            }

            ulong plusF = (vF << 1) + 1;
            int   plusE = vE - 1;

            // This code is manually inlined from the GrisuDouble.Normalize() method,
            // because the .NET JIT (at least the 64-bit one as of version 4) is too
            // incompetent to do it itself.
            const ulong k10MSBits  = 0xFFC0000000000000;
            const ulong kUint64MSB = 0x8000000000000000;

            while ((plusF & k10MSBits) == 0)
            {
                plusF <<= 10;
                plusE  -= 10;
            }
            while ((plusF & kUint64MSB) == 0)
            {
                plusF <<= 1;
                plusE--;
            }

            ulong minusF;
            int   minusE;
            bool  significand_is_zero = (vF == kHiddenBit);

            if (significand_is_zero && vE != 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.
                minusF = (vF << 2) - 1;
                minusE = vE - 2;
            }
            else
            {
                minusF = (vF << 1) - 1;
                minusE = vE - 1;
            }
            out_m_minus = new DiyFp(minusF << (minusE - plusE), plusE);
            out_m_plus  = new DiyFp(plusF, plusE);
        }
예제 #12
0
 public static DiyFp Normalize(ref DiyFp a)
 {
     DiyFp result = a;
     result.Normalize();
     return result;
 }
예제 #13
0
 // returns a * b;
 public static DiyFp Times(ref DiyFp a, ref DiyFp b)
 {
     DiyFp result = a;
     result.Multiply(ref b);
     return result;
 }
예제 #14
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;
 }
예제 #15
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)
 {
     Debug.Assert(e_ == other.e_);
     Debug.Assert(f_ >= other.f_);
     f_ -= other.f_;
 }
예제 #16
0
 public GrisuDouble(DiyFp diy_fp)
 {
     d64_ = DiyFpToUInt64(diy_fp);
     value_ = BitConverter.Int64BitsToDouble((long)d64_);
 }
예제 #17
0
 private static ulong DiyFpToUInt64(DiyFp diy_fp)
 {
     ulong significand = diy_fp.F;
     int exponent = diy_fp.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 biased_exponent;
     if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0)
     {
         biased_exponent = 0;
     }
     else
     {
         biased_exponent = (ulong)(exponent + kExponentBias);
     }
     return (significand & kSignificandMask) |
         (biased_exponent << kPhysicalSignificandSize);
 }
예제 #18
0
파일: DiyFp.cs 프로젝트: san45/czml-writer
 // 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)
 {
     Debug.Assert(e_ == other.e_);
     Debug.Assert(f_ >= other.f_);
     f_ -= other.f_;
 }
예제 #19
0
 public GrisuDouble(DiyFp diy_fp)
 {
     d64_   = DiyFpToUInt64(diy_fp);
     value_ = BitConverter.Int64BitsToDouble((long)d64_);
 }
예제 #20
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);
        }
예제 #21
0
        // Generates the digits of input number w.
        // w is a floating-point number (DiyFp), consisting of a significand and an
        // exponent. Its exponent is bounded by kMinimalTargetExponent and
        // kMaximalTargetExponent.
        //       Hence -60 <= w.e() <= -32.
        //
        // Returns false if it fails, in which case the generated digits in the buffer
        // should not be used.
        // Preconditions:
        //  * low, w and high are correct up to 1 ulp (unit in the last place). That
        //    is, their error must be less than a unit of their last digits.
        //  * low.e() == w.e() == high.e()
        //  * low < w < high, and taking into account their error: low~ <= high~
        //  * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
        // Postconditions: returns false if procedure fails.
        //   otherwise:
        //     * buffer is not null-terminated, but len contains the number of digits.
        //     * buffer contains the shortest possible decimal digit-sequence
        //       such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
        //       correct values of low and high (without their error).
        //     * if more than one decimal representation gives the minimal number of
        //       decimal digits then the one closest to W (where W is the correct value
        //       of w) is chosen.
        // Remark: this procedure takes into account the imprecision of its input
        //   numbers. If the precision is not enough to guarantee all the postconditions
        //   then false is returned. This usually happens rarely (~0.5%).
        //
        // Say, for the sake of example, that
        //   w.e() == -48, and w.f() == 0x1234567890abcdef
        // w's value can be computed by w.f() * 2^w.e()
        // We can obtain w's integral digits by simply shifting w.f() by -w.e().
        //  -> w's integral part is 0x1234
        //  w's fractional part is therefore 0x567890abcdef.
        // Printing w's integral part is easy (simply print 0x1234 in decimal).
        // In order to print its fraction we repeatedly multiply the fraction by 10 and
        // get each digit. Example the first digit after the point would be computed by
        //   (0x567890abcdef * 10) >> 48. -> 3
        // The whole thing becomes slightly more complicated because we want to stop
        // once we have enough digits. That is, once the digits inside the buffer
        // represent 'w' we can stop. Everything inside the interval low - high
        // represents w. However we have to pay attention to low, high and w's
        // imprecision.
        private static bool DigitGen(ref DiyFp low,
                                     ref DiyFp w,
                                     ref DiyFp high,
                                     char[] buffer,
                                     out int length,
                                     out int kappa)
        {
            Debug.Assert(low.E == w.E && w.E == high.E);
            Debug.Assert(low.F + 1 <= high.F - 1);
            Debug.Assert(kMinimalTargetExponent <= w.E && w.E <= kMaximalTargetExponent);
            // low, w and high are imprecise, but by less than one ulp (unit in the last
            // place).
            // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
            // the new numbers are outside of the interval we want the final
            // representation to lie in.
            // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
            // numbers that are certain to lie in the interval. We will use this fact
            // later on.
            // We will now start by generating the digits within the uncertain
            // interval. Later we will weed out representations that lie outside the safe
            // interval and thus _might_ lie outside the correct interval.
            ulong unit     = 1;
            DiyFp too_low  = new DiyFp(low.F - unit, low.E);
            DiyFp too_high = new DiyFp(high.F + unit, high.E);
            // too_low and too_high are guaranteed to lie outside the interval we want the
            // generated number in.
            DiyFp unsafe_interval = DiyFp.Minus(ref too_high, ref too_low);
            // We now cut the input number into two parts: the integral digits and the
            // fractionals. We will not write any decimal separator though, but adapt
            // kappa instead.
            // Reminder: we are currently computing the digits (stored inside the buffer)
            // such that:   too_low < buffer * 10^kappa < too_high
            // We use too_high for the digit_generation and stop as soon as possible.
            // If we stop early we effectively round down.
            DiyFp one = new DiyFp((ulong)(1) << -w.E, w.E);
            // Division by one is a shift.
            uint integrals = (uint)(too_high.F >> -one.E);
            // Modulo by one is an and.
            ulong fractionals = too_high.F & (one.F - 1);
            uint  divisor;
            int   divisor_exponent_plus_one;

            BiggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.E),
                            out divisor, out divisor_exponent_plus_one);
            kappa  = divisor_exponent_plus_one;
            length = 0;
            // Loop invariant: buffer = too_high / 10^kappa  (integer division)
            // The invariant holds for the first iteration: kappa has been initialized
            // with the divisor exponent + 1. And the divisor is the biggest power of ten
            // that is smaller than integrals.
            ulong unsafeIntervalF = unsafe_interval.F;

            while (kappa > 0)
            {
                int digit = (int)(integrals / divisor);
                buffer[length] = (char)('0' + digit);
                ++length;
                integrals %= divisor;
                kappa--;
                // Note that kappa now equals the exponent of the divisor and that the
                // invariant thus holds again.
                ulong rest =
                    ((ulong)(integrals) << -one.E) + fractionals;
                // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
                // Reminder: unsafe_interval.e() == one.e()
                if (rest < unsafeIntervalF)
                {
                    // Rounding down (by not emitting the remaining digits) yields a number
                    // that lies within the unsafe interval.
                    too_high.Subtract(ref w);
                    return(RoundWeed(buffer, length, too_high.F,
                                     unsafeIntervalF, rest,
                                     (ulong)(divisor) << -one.E, unit));
                }
                divisor /= 10;
            }

            // The integrals have been generated. We are at the point of the decimal
            // separator. In the following loop we simply multiply the remaining digits by
            // 10 and divide by one. We just need to pay attention to multiply associated
            // data (like the interval or 'unit'), too.
            // Note that the multiplication by 10 does not overflow, because w.e >= -60
            // and thus one.e >= -60.
            Debug.Assert(one.E >= -60);
            Debug.Assert(fractionals < one.F);
            Debug.Assert(0xFFFFFFFFFFFFFFFF / 10 >= one.F);
            while (true)
            {
                fractionals       *= 10;
                unit              *= 10;
                unsafe_interval.F *= 10;
                // Integer division by one.
                int digit = (int)(fractionals >> -one.E);
                buffer[length] = (char)('0' + digit);
                ++length;
                fractionals &= one.F - 1;  // Modulo by one.
                kappa--;
                if (fractionals < unsafe_interval.F)
                {
                    too_high.Subtract(ref w);
                    return(RoundWeed(buffer, length, too_high.F * unit,
                                     unsafe_interval.F, fractionals, one.F, unit));
                }
            }
        }
예제 #22
0
        // Generates the digits of input number w.
        // w is a floating-point number (DiyFp), consisting of a significand and an
        // exponent. Its exponent is bounded by kMinimalTargetExponent and
        // kMaximalTargetExponent.
        //       Hence -60 <= w.e() <= -32.
        //
        // Returns false if it fails, in which case the generated digits in the buffer
        // should not be used.
        // Preconditions:
        //  * low, w and high are correct up to 1 ulp (unit in the last place). That
        //    is, their error must be less than a unit of their last digits.
        //  * low.e() == w.e() == high.e()
        //  * low < w < high, and taking into account their error: low~ <= high~
        //  * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
        // Postconditions: returns false if procedure fails.
        //   otherwise:
        //     * buffer is not null-terminated, but len contains the number of digits.
        //     * buffer contains the shortest possible decimal digit-sequence
        //       such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
        //       correct values of low and high (without their error).
        //     * if more than one decimal representation gives the minimal number of
        //       decimal digits then the one closest to W (where W is the correct value
        //       of w) is chosen.
        // Remark: this procedure takes into account the imprecision of its input
        //   numbers. If the precision is not enough to guarantee all the postconditions
        //   then false is returned. This usually happens rarely (~0.5%).
        //
        // Say, for the sake of example, that
        //   w.e() == -48, and w.f() == 0x1234567890abcdef
        // w's value can be computed by w.f() * 2^w.e()
        // We can obtain w's integral digits by simply shifting w.f() by -w.e().
        //  -> w's integral part is 0x1234
        //  w's fractional part is therefore 0x567890abcdef.
        // Printing w's integral part is easy (simply print 0x1234 in decimal).
        // In order to print its fraction we repeatedly multiply the fraction by 10 and
        // get each digit. Example the first digit after the point would be computed by
        //   (0x567890abcdef * 10) >> 48. -> 3
        // The whole thing becomes slightly more complicated because we want to stop
        // once we have enough digits. That is, once the digits inside the buffer
        // represent 'w' we can stop. Everything inside the interval low - high
        // represents w. However we have to pay attention to low, high and w's
        // imprecision.
        private static bool DigitGen(ref DiyFp low,
                             ref DiyFp w,
                             ref DiyFp high,
                             char[] buffer,
                             out int length,
                             out int kappa)
        {
            Debug.Assert(low.E == w.E && w.E == high.E);
            Debug.Assert(low.F + 1 <= high.F - 1);
            Debug.Assert(kMinimalTargetExponent <= w.E && w.E <= kMaximalTargetExponent);
            // low, w and high are imprecise, but by less than one ulp (unit in the last
            // place).
            // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
            // the new numbers are outside of the interval we want the final
            // representation to lie in.
            // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
            // numbers that are certain to lie in the interval. We will use this fact
            // later on.
            // We will now start by generating the digits within the uncertain
            // interval. Later we will weed out representations that lie outside the safe
            // interval and thus _might_ lie outside the correct interval.
            ulong unit = 1;
            DiyFp too_low = new DiyFp(low.F - unit, low.E);
            DiyFp too_high = new DiyFp(high.F + unit, high.E);
            // too_low and too_high are guaranteed to lie outside the interval we want the
            // generated number in.
            DiyFp unsafe_interval = DiyFp.Minus(ref too_high, ref too_low);
            // We now cut the input number into two parts: the integral digits and the
            // fractionals. We will not write any decimal separator though, but adapt
            // kappa instead.
            // Reminder: we are currently computing the digits (stored inside the buffer)
            // such that:   too_low < buffer * 10^kappa < too_high
            // We use too_high for the digit_generation and stop as soon as possible.
            // If we stop early we effectively round down.
            DiyFp one = new DiyFp((ulong)(1) << -w.E, w.E);
            // Division by one is a shift.
            uint integrals = (uint)(too_high.F >> -one.E);
            // Modulo by one is an and.
            ulong fractionals = too_high.F & (one.F - 1);
            uint divisor;
            int divisor_exponent_plus_one;
            BiggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.E),
                            out divisor, out divisor_exponent_plus_one);
            kappa = divisor_exponent_plus_one;
            length = 0;
            // Loop invariant: buffer = too_high / 10^kappa  (integer division)
            // The invariant holds for the first iteration: kappa has been initialized
            // with the divisor exponent + 1. And the divisor is the biggest power of ten
            // that is smaller than integrals.
            ulong unsafeIntervalF = unsafe_interval.F;
            while (kappa > 0)
            {
                int digit = (int)(integrals / divisor);
                buffer[length] = (char)('0' + digit);
                ++length;
                integrals %= divisor;
                kappa--;
                // Note that kappa now equals the exponent of the divisor and that the
                // invariant thus holds again.
                ulong rest =
                    ((ulong)(integrals) << -one.E) + fractionals;
                // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
                // Reminder: unsafe_interval.e() == one.e()
                if (rest < unsafeIntervalF)
                {
                    // Rounding down (by not emitting the remaining digits) yields a number
                    // that lies within the unsafe interval.
                    too_high.Subtract(ref w);
                    return RoundWeed(buffer, length, too_high.F,
                                     unsafeIntervalF, rest,
                                     (ulong)(divisor) << -one.E, unit);
                }
                divisor /= 10;
            }

            // The integrals have been generated. We are at the point of the decimal
            // separator. In the following loop we simply multiply the remaining digits by
            // 10 and divide by one. We just need to pay attention to multiply associated
            // data (like the interval or 'unit'), too.
            // Note that the multiplication by 10 does not overflow, because w.e >= -60
            // and thus one.e >= -60.
            Debug.Assert(one.E >= -60);
            Debug.Assert(fractionals < one.F);
            Debug.Assert(0xFFFFFFFFFFFFFFFF / 10 >= one.F);
            while (true)
            {
                fractionals *= 10;
                unit *= 10;
                unsafe_interval.F *= 10;
                // Integer division by one.
                int digit = (int)(fractionals >> -one.E);
                buffer[length] = (char)('0' + digit);
                ++length;
                fractionals &= one.F - 1;  // Modulo by one.
                kappa--;
                if (fractionals < unsafe_interval.F)
                {
                    too_high.Subtract(ref w);
                    return RoundWeed(buffer, length, too_high.F * unit,
                                     unsafe_interval.F, fractionals, one.F, unit);
                }
            }
        }
예제 #23
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 Double must be greater than 0.
        public void NormalizedBoundaries(out DiyFp out_m_minus, out DiyFp out_m_plus)
        {
            Debug.Assert(Value > 0.0);

            ulong d64 = d64_;
            ulong vF;
            int vE;
            if (IsDenormal)
            {
                vF = d64 & kSignificandMask;
                vE = kDenormalExponent;
            }
            else
            {
                vF = (d64 & kSignificandMask) + kHiddenBit;
                vE = (int)((d64 & kExponentMask) >> kPhysicalSignificandSize) - kExponentBias;
            }

            ulong plusF = (vF << 1) + 1;
            int plusE = vE - 1;

            // This code is manually inlined from the GrisuDouble.Normalize() method,
            // because the .NET JIT (at least the 64-bit one as of version 4) is too
            // incompetent to do it itself.
            const ulong k10MSBits = 0xFFC0000000000000;
            const ulong kUint64MSB = 0x8000000000000000;
            while ((plusF & k10MSBits) == 0)
            {
                plusF <<= 10;
                plusE -= 10;
            }
            while ((plusF & kUint64MSB) == 0)
            {
                plusF <<= 1;
                plusE--;
            }

            ulong minusF;
            int minusE;
            bool significand_is_zero = (vF == kHiddenBit);
            if (significand_is_zero && vE != 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.
                minusF = (vF << 2) - 1;
                minusE = vE - 2;
            }
            else
            {
                minusF = (vF << 1) - 1;
                minusE = vE - 1;
            }
            out_m_minus = new DiyFp(minusF << (minusE - plusE), plusE);
            out_m_plus = new DiyFp(plusF, plusE);
        }