Example #1
0
            public void Subtract(DiyFp other)
            {
                Debug.Assert(Exponent == other.Exponent);
                Debug.Assert(Significand >= other.Significand);

                Significand -= other.Significand;
            }
Example #2
0
            public static DiyFp Minus(DiyFp a, DiyFp b)
            {
                var result = a;

                result.Subtract(b);

                return(result);
            }
Example #3
0
            public static DiyFp Normalize(DiyFp a)
            {
                var result = a;

                result.Normalize();

                return(result);
            }
Example #4
0
            public static DiyFp Times(DiyFp a, DiyFp b)
            {
                var result = a;

                result.Multiply(b);

                return(result);
            }
Example #5
0
            public void NormalizedBoundaries(out DiyFp minus, out DiyFp plus)
            {
                Debug.Assert(Value() > 0.0);

                var v = AsDiyFp();

                plus  = DiyFp.Normalize(new DiyFp((v.Significand << 1) + 1, v.Exponent - 1));
                minus = IsLowerBoundaryCloser() ?
                        new DiyFp((v.Significand << 2) - 1, v.Exponent - 2) :
                        new DiyFp((v.Significand << 1) - 1, v.Exponent - 1);

                minus.Significand = minus.Significand << (minus.Exponent - plus.Exponent);
                minus.Exponent    = plus.Exponent;
            }
Example #6
0
        // Returns a cached power-of-ten with a binary exponent in the range
        // [min_exponent; max_exponent] (boundaries included).
        private static void GetCachedPowerForBinaryExponentRange(Int32 min_exponent, Int32 max_exponent, out DiyFp power, out Int32 decimal_exponent)
        {
            const Double D_1_LOG2_10 = 0.30102999566398114;  //  1 / lg(10)

            var kQ           = DiyFp.SignificantSize;
            var k            = Math.Ceiling((min_exponent + kQ - 1) * D_1_LOG2_10);
            var index        = (CACHED_POWERS_OFFSET + (Int32)(k) - 1) / DECIMAL_EXPONENT_DISTANCE + 1;
            var cached_power = CACHED_POWERS[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);
        }
Example #7
0
            public 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 UInt64 M32 = 0xFFFFFFFFU;

                var a   = Significand >> 32;
                var b   = Significand & M32;
                var c   = other.Significand >> 32;
                var d   = other.Significand & M32;
                var ac  = a * c;
                var bc  = b * c;
                var ad  = a * d;
                var bd  = b * d;
                var tmp = (bd >> 32) + (ad & M32) + (bc & M32);

                // By adding 1U << 31 to tmp we round the final result.
                // Halfway cases will be round up.
                tmp += 1U << 31;

                Exponent   += other.Exponent + 64;
                Significand = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
            }
Example #8
0
        private static Boolean DigitGen(DiyFp low,
                                        DiyFp w,
                                        DiyFp high,
                                        Span <Byte> buffer,
                                        out Int32 length,
                                        out Int32 kappa)
        {
            Debug.Assert(low.Exponent == w.Exponent && w.Exponent == high.Exponent);
            Debug.Assert(low.Significand + 1 <= high.Significand - 1);
            Debug.Assert(MINIMAL_TARGET_EXPONENT <= w.Exponent && w.Exponent <= MAXIMAL_TARGET_EXPONENT);

            // 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.
            UInt64 unit     = 1;
            var    too_low  = new DiyFp(low.Significand - unit, low.Exponent);
            var    too_high = new DiyFp(high.Significand + unit, high.Exponent);
            // too_low and too_high are guaranteed to lie outside the interval we want the generated number in.
            var unsafe_interval = DiyFp.Minus(too_high, 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.
            var one = new DiyFp(1UL << -w.Exponent, w.Exponent);
            // Division by one is a shift.
            var integrals = (UInt32)(too_high.Significand >> -one.Exponent);
            // Modulo by one is an and.
            var fractionals = too_high.Significand & (one.Significand - 1);

            BiggestPowerTen(integrals, DiyFp.SignificantSize - (-one.Exponent), out var divisor, out var 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.
            while (kappa > 0)
            {
                var digit = integrals / divisor;
                Debug.Assert(digit <= 9);
                buffer[length] = (Byte)('0' + digit);
                ++length;
                integrals %= divisor;
                --kappa;
                // Note that kappa now equals the exponent of the divisor and that the
                // invariant thus holds again.
                var rest = ((UInt64)(integrals) << -one.Exponent) + fractionals;
                // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
                // Reminder: unsafe_interval.e() == one.e()
                if (rest < unsafe_interval.Significand)
                {
                    // Rounding down (by not emitting the remaining digits) yields a number
                    // that lies within the unsafe interval.
                    return(RoundWeed(buffer,
                                     length,
                                     DiyFp.Minus(too_high, w).Significand,
                                     unsafe_interval.Significand,
                                     rest,
                                     (UInt64)(divisor) << -one.Exponent,
                                     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.Exponent >= -60);
            Debug.Assert(fractionals < one.Significand);
            Debug.Assert(0xFFFFFFFF_FFFFFFFF / 10 >= one.Significand);

            while (true)
            {
                fractionals *= 10;
                unit        *= 10;
                unsafe_interval.Significand = unsafe_interval.Significand * 10;
                // Integer division by one.
                var digit = (Int32)(fractionals >> -one.Exponent);
                Debug.Assert(digit <= 9);

                buffer[length] = (Byte)('0' + digit);
                ++length;
                fractionals &= one.Significand - 1;  // Modulo by one.
                --kappa;

                if (fractionals < unsafe_interval.Significand)
                {
                    return(RoundWeed(buffer,
                                     length,
                                     DiyFp.Minus(too_high, w).Significand *unit,
                                     unsafe_interval.Significand,
                                     fractionals,
                                     one.Significand,
                                     unit));
                }
            }
        }
Example #9
0
        private static Boolean Grisu3(Double v, Boolean single_precision, Span <Byte> buffer, out Int32 length, out Int32 decimal_exponent)
        {
            var w = new DiyDouble(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;

            if (single_precision)
            {
                new DiySingle((Single)v).NormalizedBoundaries(out boundary_minus, out boundary_plus);
            }
            else
            {
                new DiyDouble(v).NormalizedBoundaries(out boundary_minus, out boundary_plus);
            }

            Debug.Assert(boundary_plus.Exponent == w.Exponent);

            var ten_mk_minimal_binary_exponent = MINIMAL_TARGET_EXPONENT - (w.Exponent + DiyFp.SignificantSize);
            var ten_mk_maximal_binary_exponent = MAXIMAL_TARGET_EXPONENT - (w.Exponent + DiyFp.SignificantSize);

            // 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.
            GetCachedPowerForBinaryExponentRange(ten_mk_minimal_binary_exponent,
                                                 ten_mk_maximal_binary_exponent,
                                                 out var ten_mk,
                                                 out var mk);

            Debug.Assert(MINIMAL_TARGET_EXPONENT <= w.Exponent + ten_mk.Exponent + DiyFp.SignificantSize);
            Debug.Assert(MAXIMAL_TARGET_EXPONENT >= w.Exponent + ten_mk.Exponent + DiyFp.SignificantSize);

            // 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 scaled_w = DiyFp.Times(w, ten_mk);

            Debug.Assert(scaled_w.Exponent == boundary_plus.Exponent + ten_mk.Exponent + DiyFp.SignificantSize);

            // 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.
            var scaled_boundary_minus = DiyFp.Times(boundary_minus, ten_mk);
            var scaled_boundary_plus  = DiyFp.Times(boundary_plus, 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.
            var result = DigitGen(scaled_boundary_minus,
                                  scaled_w,
                                  scaled_boundary_plus,
                                  buffer,
                                  out length,
                                  out var kappa);

            decimal_exponent = -mk + kappa;

            return(result);
        }