System.Xml.Xsl.XPathConvert.BigNumber.DblToRgbFast C# (CSharp) Method

DblToRgbFast() public static method

public static DblToRgbFast ( double dbl, byte mantissa, int &exponent, int &mantissaSize ) : bool
dbl double
mantissa byte
exponent int
mantissaSize int
return bool
            public static bool DblToRgbFast(double dbl, byte[] mantissa, out int exponent, out int mantissaSize) {
                BigNumber numHH, numHL, numLH, numLL;
                BigNumber numBase;
                BigNumber tenPower;
                int ib, iT;
                uint uT, uScale;
                byte bHH, bHL, bLH, bLL;
                uint uHH, uHL, uLH, uLL;
                int wExp2, wExp10 = 0;
                double dblInt;
                uint dblHi, dblLo;

                dblHi = DblHi(dbl);
                dblLo = DblLo(dbl);

                // Caller should take care of 0, negative and non-finite values.
                Debug.Assert(!IsSpecial(dbl));
                Debug.Assert(0 < dbl);

                // Get numHH and numLL such that numLL < dbl < numHH and the
                // difference between adjacent values is half the distance to the next
                // representable value (in a double).
                wExp2 = (int)((dblHi >> 20) & 0x07FF);
                if (wExp2 > 0) {
                    // See if dbl is a small integer.
                    if (wExp2 >= 1023 && wExp2 <= 1075 && dbl == Math.Floor(dbl)) {
                        goto LSmallInt;
                    }

                    // Normalized
                    numBase.u2 = 0x80000000 | ((dblHi & 0x000FFFFFF) << 11) | (dblLo >> 21);
                    numBase.u1 = dblLo << 11;
                    numBase.u0 = 0;
                    numBase.exp = wExp2 - 1022;
                    numBase.error = 0;

                    // Get the upper bound
                    numHH = numBase;
                    numHH.u1 |= (1 << 10);

                    // Get the lower bound. A power of 2 must be special cased.
                    numLL = numBase;
                    if (0x80000000 == numLL.u2 && 0 == numLL.u1) {
                        // Subtract (0x00000000, 0x00000200, 0x00000000). Same as adding
                        // (0xFFFFFFFF, 0xFFFFFE00, 0x00000000)
                        uT = 0xFFFFFE00;
                    } else {
                        // Subtract (0x00000000, 0x00000400, 0x00000000). Same as adding
                        // (0xFFFFFFFF, 0xFFFFFC00, 0x00000000)
                        uT = 0xFFFFFC00;
                    }
                    if (0 == AddU(ref numLL.u1, uT)) {
                        AddU(ref numLL.u2, 0xFFFFFFFF);
                        if (0 == (0x80000000 & numLL.u2)) {
                            numLL.Normalize();
                        }
                    }
                } else {
                    // Denormal
                    numBase.u2 = dblHi & 0x000FFFFF;
                    numBase.u1 = dblLo;
                    numBase.u0 = 0;
                    numBase.exp = -1010;
                    numBase.error = 0;

                    // Get the upper bound
                    numHH = numBase;
                    numHH.u0 = 0x80000000;

                    // Get the lower bound
                    numLL = numHH;
                    if (0 == AddU(ref numLL.u1, 0xFFFFFFFF)) {
                        AddU(ref numLL.u2, 0xFFFFFFFF);
                    }

                    numBase.Normalize();
                    numHH.Normalize();
                    numLL.Normalize();
                }

                // Multiply by powers of ten until 0 < numHH.exp < 32.
                if (numHH.exp >= 32) {
                    iT = (numHH.exp - 25) * 15 / -TenPowersNeg[45].exp;
                    Debug.Assert(iT >= 0 && iT < 16);
                    if (iT > 0) {
                        tenPower = TenPowersNeg[30 + iT];
                        Debug.Assert(numHH.exp + tenPower.exp > 1);
                        numHH.Mul(ref tenPower);
                        numLL.Mul(ref tenPower);
                        wExp10 += iT * 32;
                    }

                    if (numHH.exp >= 32) {
                        iT = (numHH.exp - 25) * 32 / -TenPowersNeg[31].exp;
                        Debug.Assert(iT > 0 && iT <= 32);
                        tenPower = TenPowersNeg[iT - 1];
                        Debug.Assert(numHH.exp + tenPower.exp > 1);
                        numHH.Mul(ref tenPower);
                        numLL.Mul(ref tenPower);
                        wExp10 += iT;
                    }
                } else if (numHH.exp < 1) {
                    iT = (25 - numHH.exp) * 15 / TenPowersPos[45].exp;
                    Debug.Assert(iT >= 0 && iT < 16);
                    if (iT > 0) {
                        tenPower = TenPowersPos[30 + iT];
                        Debug.Assert(numHH.exp + tenPower.exp <= 32);
                        numHH.Mul(ref tenPower);
                        numLL.Mul(ref tenPower);
                        wExp10 -= iT * 32;
                    }

                    if (numHH.exp < 1) {
                        iT = (25 - numHH.exp) * 32 / TenPowersPos[31].exp;
                        Debug.Assert(iT > 0 && iT <= 32);
                        tenPower = TenPowersPos[iT - 1];
                        Debug.Assert(numHH.exp + tenPower.exp <= 32);
                        numHH.Mul(ref tenPower);
                        numLL.Mul(ref tenPower);
                        wExp10 -= iT;
                    }
                }

                Debug.Assert(numHH.exp > 0 && numHH.exp < 32);

                // Get the upper and lower bounds for these.
                numHL = numHH;
                numHH.MakeUpperBound();
                numHL.MakeLowerBound();
                uHH = numHH.UMod1();
                uHL = numHL.UMod1();
                numLH = numLL;
                numLH.MakeUpperBound();
                numLL.MakeLowerBound();
                uLH = numLH.UMod1();
                uLL = numLL.UMod1();
                Debug.Assert(uLL <= uLH && uLH <= uHL && uHL <= uHH);

                // Find the starting scale
                uScale = 1;
                if (uHH >= 100000000) {
                    uScale = 100000000;
                    wExp10 += 8;
                } else {
                    if (uHH >= 10000) {
                        uScale = 10000;
                        wExp10 += 4;
                    }
                    if (uHH >= 100 * uScale) {
                        uScale *= 100;
                        wExp10 += 2;
                    }
                }
                if (uHH >= 10 * uScale) {
                    uScale *= 10;
                    wExp10++;
                }
                wExp10++;
                Debug.Assert(uHH >= uScale && uHH / uScale < 10);

                for (ib = 0; ; ) {
                    Debug.Assert(uLL <= uHH);
                    bHH = (byte)(uHH / uScale);
                    uHH %= uScale;
                    bLL = (byte)(uLL / uScale);
                    uLL %= uScale;

                    if (bHH != bLL) {
                        break;
                    }

                    Debug.Assert(0 != uHH || !numHH.IsZero);
                    mantissa[ib++] = bHH;

                    if (1 == uScale) {
                        // Multiply by 10^8.
                        uScale = 10000000;

                        numHH.Mul(ref TenPowersPos[7]);
                        numHH.MakeUpperBound();
                        uHH = numHH.UMod1();
                        if (uHH >= 100000000) {
                            goto LFail;
                        }
                        numHL.Mul(ref TenPowersPos[7]);
                        numHL.MakeLowerBound();
                        uHL = numHL.UMod1();

                        numLH.Mul(ref TenPowersPos[7]);
                        numLH.MakeUpperBound();
                        uLH = numLH.UMod1();
                        numLL.Mul(ref TenPowersPos[7]);
                        numLL.MakeLowerBound();
                        uLL = numLL.UMod1();
                    } else {
                        uScale /= 10;
                    }
                }

                // LL and HH diverged. Get the digit values for LH and HL.
                Debug.Assert(0 <= bLL && bLL < bHH && bHH <= 9);
                bLH = (byte)((uLH / uScale) % 10);
                uLH %= uScale;
                bHL = (byte)((uHL / uScale) % 10);
                uHL %= uScale;

                if (bLH >= bHL) {
                    goto LFail;
                }

                // LH and HL also diverged.

                // We can get by with one fewer digit if: LL == LH and bLH is zero
                // and the current value of LH is zero and the least significant bit of
                // the double is zero. In this case, we have exactly the digit sequence
                // for the original numLL and IEEE and will rounds numLL up to the double.
                if (0 == bLH && 0 == uLH && numLH.IsZero && 0 == (dblLo & 1)) {
                }
                else if (bHL - bLH > 1) {
                    // HL and LH differ by at least two in this digit, so split
                    // the difference.
                    mantissa[ib++] = (byte)((bHL + bLH + 1) / 2);
                } else if (0 != uHL || !numHL.IsZero || 0 == (dblLo & 1)) {
                    // We can just use bHL because this guarantees that we're bigger than
                    // LH and less than HL, so must convert to the double.
                    mantissa[ib++] = bHL;
                } else {
                    goto LFail;
                }

                exponent = wExp10;
                mantissaSize = ib;
                goto LSucceed;

            LSmallInt:
                // dbl should be an integer from 1 to (2^53 - 1).
                dblInt = dbl;
                Debug.Assert(dblInt == Math.Floor(dblInt) && 1 <= dblInt && dblInt <= 9007199254740991.0d);

                iT = 0;
                if (dblInt >= C10toN[iT + 8]) {
                    iT += 8;
                }
                if (dblInt >= C10toN[iT + 4]) {
                    iT += 4;
                }
                if (dblInt >= C10toN[iT + 2]) {
                    iT += 2;
                }
                if (dblInt >= C10toN[iT + 1]) {
                    iT += 1;
                }
                Debug.Assert(iT >= 0 && iT <= 15);
                Debug.Assert(dblInt >= C10toN[iT] && dblInt < C10toN[iT + 1]);
                exponent = iT + 1;

                for (ib = 0; 0 != dblInt; iT--) {
                    Debug.Assert(iT >= 0);
                    bHH = (byte)(dblInt / C10toN[iT]);
                    dblInt -= bHH * C10toN[iT];
                    Debug.Assert(dblInt == Math.Floor(dblInt) && 0 <= dblInt && dblInt < C10toN[iT]);
                    mantissa[ib++] = bHH;
                }
                mantissaSize = ib;

            LSucceed:

            #if DEBUG
                // Verify that precise is working and gives the same answer
                if (mantissaSize > 0) {
                    byte[] mantissaPrec = new byte[20];
                    int    exponentPrec, mantissaSizePrec, idx;

                    DblToRgbPrecise(dbl, mantissaPrec, out exponentPrec, out mantissaSizePrec);
                    Debug.Assert(exponent == exponentPrec && mantissaSize == mantissaSizePrec);
                    // Assert(!memcmp(mantissa, mantissaPrec, mantissaSizePrec - 1));
                    bool equal = true;
                    for (idx = 0; idx < mantissaSize; idx++) {
                        equal &= (
                            (mantissa[idx] == mantissaPrec[idx]) ||
                            (idx == mantissaSize - 1) && Math.Abs(mantissa[idx] - mantissaPrec[idx]) <= 1
                        );
                    }
                    Debug.Assert(equal, "DblToRgbFast and DblToRgbPrecise should give the same result");
                }
            #endif

                return true;

            LFail:
                exponent = mantissaSize = 0;
                return false;
            }