Esempio n. 1
0
        /// <summary>
        /// Creates a Ryu float from an IEEE 32 bit float.
        /// </summary>
        public RyuFloat32(bool fSign, uint ieeeMantissa, uint ieeeExponent)
        {
            const int FLOAT_EXP_DIFF_P2 = FLOAT_BIAS + FLOAT_MANTISSA_BITS + 2;
            int       exponent;
            uint      mantissa;

            if (ieeeExponent == 0U)
            {
                // Subtract 2 so that the bounds computation has 2 additional bits
                exponent = 1 - FLOAT_EXP_DIFF_P2;
                mantissa = ieeeMantissa;
            }
            else
            {
                exponent = (int)ieeeExponent - FLOAT_EXP_DIFF_P2;
                mantissa = (1U << FLOAT_MANTISSA_BITS) | ieeeMantissa;
            }
            bool even = (mantissa & 1U) == 0U, acceptBounds = even, mmShift =
                ieeeMantissa != 0 || ieeeExponent <= 1;

            // Step 2: Determine the interval of valid decimal representations
            uint mv = mantissa << 2, mp = mv + 2U, mm = mv - 1U - (mmShift ? 1U : 0U);

            // Step 3: Convert to a decimal power base using 64-bit arithmetic
            uint vr, vp, vm;
            int  e10;
            bool vmTrailingZeroes = false, vrTrailingZeroes = false;
            uint removedDigit = 0U;

            if (exponent >= 0)
            {
                int q = RyuUtils.Log10Pow2(exponent), k = FLOAT_POW5_INV_BITCOUNT + RyuUtils.
                                                          Pow5Bits(q) - 1, i = -exponent + q + k;
                e10 = q;
                vr  = RyuTables.MulPow5InvDivPow2(mv, q, i);
                vp  = RyuTables.MulPow5InvDivPow2(mp, q, i);
                vm  = RyuTables.MulPow5InvDivPow2(mm, q, i);
                if (q != 0U && (vp - 1U) / 10U <= vm / 10U)
                {
                    // We need to know one removed digit even if we are not going to loop
                    // below. We could use q = X - 1 above, except that would require 33 bits
                    // for the result, and we've found that 32-bit arithmetic is faster even on
                    // 64-bit machines
                    int l = FLOAT_POW5_INV_BITCOUNT + RyuUtils.Pow5Bits(q - 1) - 1;
                    removedDigit = RyuTables.MulPow5InvDivPow2(mv, q - 1, -exponent +
                                                               q - 1 + l) % 10U;
                }
                if (q <= 9U)
                {
                    // The largest power of 5 that fits in 24 bits is 5^10, but q <= 9 seems to
                    // be safe as well. Only one of mp, mv, and mm can be a multiple of 5, if
                    // any
                    if (mv % 5U == 0U)
                    {
                        vrTrailingZeroes = RyuUtils.IsMultipleOf5Power(mv, q);
                    }
                    else if (acceptBounds)
                    {
                        vmTrailingZeroes = RyuUtils.IsMultipleOf5Power(mm, q);
                    }
                    else if (RyuUtils.IsMultipleOf5Power(mp, q))
                    {
                        vp -= 1U;
                    }
                }
            }
            else
            {
                int q = RyuUtils.Log10Pow5(-exponent), i = -exponent - q, k = RyuUtils.
                                                                              Pow5Bits(i) - FLOAT_POW5_BITCOUNT, j = q - k;
                e10 = q + exponent;
                vr  = RyuTables.MulPow5DivPow2(mv, i, j);
                vp  = RyuTables.MulPow5DivPow2(mp, i, j);
                vm  = RyuTables.MulPow5DivPow2(mm, i, j);
                if (q != 0U && (vp - 1U) / 10U <= vm / 10U)
                {
                    j            = q - 1 - (RyuUtils.Pow5Bits(i + 1) - FLOAT_POW5_BITCOUNT);
                    removedDigit = RyuTables.MulPow5DivPow2(mv, i + 1, j) % 10U;
                }
                if (q <= 1U)
                {
                    // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0
                    // bits. mv = 4 * m2, so it always has at least two trailing 0 bits
                    vrTrailingZeroes = true;
                    if (acceptBounds)
                    {
                        // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1
                        vmTrailingZeroes = mmShift;
                    }
                    else
                    {
                        // mp = mv + 2, so it always has at least one trailing 0 bit
                        --vp;
                    }
                }
                else if (q < 31U)
                {
                    vrTrailingZeroes = RyuUtils.IsMultipleOf2Power(mv, q - 1);
                }
            }

            // Step 4: Find the shortest decimal representation in the interval of valid
            // representations
            int  removed = 0;
            uint output, m10, p10;

            if (vmTrailingZeroes || vrTrailingZeroes)
            {
                // General case, which happens rarely (~4.0%)
                while ((p10 = vp / 10U) > (m10 = vm.DivMod(10U, out uint vmRem)))
                {
                    if (vmRem != 0U)
                    {
                        vmTrailingZeroes = false;
                    }
                    if (removedDigit != 0U)
                    {
                        vrTrailingZeroes = false;
                    }
                    vr = vr.DivMod(10U, out removedDigit);
                    vp = p10;
                    vm = m10;
                    removed++;
                }
                if (vmTrailingZeroes)
                {
                    while ((vm - 10U * (m10 = vm / 10U)) == 0U)
                    {
                        if (removedDigit != 0U)
                        {
                            vrTrailingZeroes = false;
                        }
                        vr  = vr.DivMod(10U, out removedDigit);
                        vp /= 10U;
                        vm  = m10;
                        removed++;
                    }
                }
                if (vrTrailingZeroes && removedDigit == 5U && vr % 2U == 0U)
                {
                    // Round even if the exact number is .....50..0
                    removedDigit = 4U;
                }
                // We need to take vr + 1 if vr is outside bounds or we need to round up
                output = vr;
                if ((vr == vm && (!acceptBounds || !vmTrailingZeroes)) || removedDigit >= 5U)
                {
                    output++;
                }
            }
            else
            {
                // Specialized for the common case (~96.0%). Percentages below are relative to
                // this. Loop iterations below (approximately):
                // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01%
                while (RyuUtils.DivCompare10(ref vp, ref vm))
                {
                    vr = vr.DivMod(10U, out removedDigit);
                    removed++;
                }
                // We need to take vr + 1 if vr is outside bounds or we need to round up
                output = vr;
                if (vr == vm || removedDigit >= 5)
                {
                    output++;
                }
            }
            sign          = fSign;
            this.mantissa = output;
            this.exponent = e10 + removed;
        }