Exemplo n.º 1
0
            /*  ----------------------------------------------------------------------------
                DblToRgbPrecise()

                Uses big integer arithmetic to get the sequence of digits.
            */
            public static void DblToRgbPrecise(double dbl, byte[] mantissa, out int exponent, out int mantissaSize) {
                BigInteger biNum = new BigInteger();
                BigInteger biDen = new BigInteger();
                BigInteger biHi  = new BigInteger();
                BigInteger biLo  = new BigInteger();
                BigInteger biT   = new BigInteger();
                BigInteger biHiLo;
                byte bT;
                bool fPow2;
                int ib, cu;
                int wExp10, wExp2, w1, w2;
                int c2Num, c2Den, c5Num, c5Den;
                double dblT;
                //uint *rgu = stackalloc uint[2];
                uint rgu0, rgu1;
                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);

                // Init the Denominator, Hi error and Lo error bigints.
                biDen.InitFromDigits(1, 0, 1);
                biHi.InitFromDigits(1, 0, 1);

                wExp2 = (int)(((dblHi & 0x7FF00000) >> 20) - 1075);
                rgu1 = dblHi & 0x000FFFFF;
                rgu0 = dblLo;
                cu = 2;
                fPow2 = false;
                if (wExp2 == -1075) {
                    // dbl is denormalized.
                    Debug.Assert(0 == (dblHi & 0x7FF00000));
                    if (0 == rgu1) {
                        cu = 1;
                    }

                    // Get dblT such that dbl / dblT is a power of 2 and 1 <= dblT < 2.
                    // First multiply by a power of 2 to get a normalized value.
                    dblT = BitConverter.Int64BitsToDouble(0x4FF00000L << 32);
                    dblT *= dbl;
                    Debug.Assert(0 != (DblHi(dblT) & 0x7FF00000));

                    // This is the power of 2.
                    w1 = (int)((DblHi(dblT) & 0x7FF00000) >> 20) - (256 + 1023);

                    dblHi = DblHi(dblT);
                    dblHi &= 0x000FFFFF;
                    dblHi |= 0x3FF00000;
                    dblT = BitConverter.Int64BitsToDouble((long)dblHi << 32 | DblLo(dblT));

                    // Adjust wExp2 because we don't have the implicit bit.
                    wExp2++;
                } else {
                    // Get dblT such that dbl / dblT is a power of 2 and 1 <= dblT < 2.
                    // First multiply by a power of 2 to get a normalized value.
                    dblHi &= 0x000FFFFF;
                    dblHi |= 0x3FF00000;
                    dblT = BitConverter.Int64BitsToDouble((long)dblHi << 32 | dblLo);

                    // This is the power of 2.
                    w1 = wExp2 + 52;

                    if (0 == rgu0 && 0 == rgu1 && wExp2 > -1074) {
                        // Power of 2 bigger than smallest normal. The next smaller
                        // representable value is closer than the next larger value.
                        rgu1 = 0x00200000;
                        wExp2--;
                        fPow2 = true;
                    } else {
                        // Normalized and not a power of 2 or the smallest normal. The
                        // representable values on either side are the same distance away.
                        rgu1 |= 0x00100000;
                    }
                }

                // Compute an approximation to the base 10 log. This is borrowed from
                // David ----'s paper.
                Debug.Assert(1 <= dblT && dblT < 2);
                dblT = (dblT - 1.5) * 0.289529654602168 + 0.1760912590558 +
                    w1 * 0.301029995663981;
                wExp10 = (int)dblT;
                if (dblT < 0 && dblT != wExp10) {
                    wExp10--;
                }

                if (wExp2 >= 0) {
                    c2Num = wExp2;
                    c2Den = 0;
                } else {
                    c2Num = 0;
                    c2Den = -wExp2;
                }

                if (wExp10 >= 0) {
                    c5Num = 0;
                    c5Den = wExp10;
                    c2Den += wExp10;
                } else {
                    c2Num -= wExp10;
                    c5Num = -wExp10;
                    c5Den = 0;
                }

                if (c2Num > 0 && c2Den > 0) {
                    w1 = c2Num < c2Den ? c2Num : c2Den;
                    c2Num -= w1;
                    c2Den -= w1;
                }
                // We need a bit for the Hi and Lo values.
                c2Num++;
                c2Den++;

                // Initialize biNum and multiply by powers of 5.
                if (c5Num > 0) {
                    Debug.Assert(0 == c5Den);
                    biHi.MulPow5(c5Num);
                    biNum.InitFromBigint(biHi);
                    if (1 == cu) {
                        biNum.MulAdd(rgu0, 0);
                    } else {
                        biNum.MulAdd(rgu1, 0);
                        biNum.ShiftLeft(32);
                        if (0 != rgu0) {
                            biT.InitFromBigint(biHi);
                            biT.MulAdd(rgu0, 0);
                            biNum.Add(biT);
                        }
                    }
                } else {
                    Debug.Assert(cu <= 2);
                    biNum.InitFromDigits(rgu0, rgu1, cu);
                    if (c5Den > 0) {
                        biDen.MulPow5(c5Den);
                    }
                }

                // BigInteger.DivRem only works if the 4 high bits of the divisor are 0.
                // It works most efficiently if there are exactly 4 zero high bits.
                // Adjust c2Den and c2Num to guarantee this.
                w1 = CbitZeroLeft(biDen[biDen.Length - 1]);
                w1 = (w1 + 28 - c2Den) & 0x1F;
                c2Num += w1;
                c2Den += w1;

                // Multiply by powers of 2.
                Debug.Assert(c2Num > 0 && c2Den > 0);
                biNum.ShiftLeft(c2Num);
                if (c2Num > 1) {
                    biHi.ShiftLeft(c2Num - 1);
                }
                biDen.ShiftLeft(c2Den);
                Debug.Assert(0 == (biDen[biDen.Length - 1] & 0xF0000000));
                Debug.Assert(0 != (biDen[biDen.Length - 1] & 0x08000000));

                // Get biHiLo and handle the power of 2 case where biHi needs to be doubled.
                if (fPow2) {
                    biHiLo = biLo;
                    biHiLo.InitFromBigint(biHi);
                    biHi.ShiftLeft(1);
                } else {
                    biHiLo = biHi;
                }

                for (ib = 0; ; ) {
                    bT = (byte)biNum.DivRem(biDen);
                    if (0 == ib && 0 == bT) {
                        // Our estimate of wExp10 was too big. Oh well.
                        wExp10--;
                        goto LSkip;
                    }

                    // w1 = sign(biNum - biHiLo).
                    w1 = biNum.CompareTo(biHiLo);

                    // w2 = sign(biNum + biHi - biDen).
                    if (biDen.CompareTo(biHi) < 0) {
                        w2 = 1;
                    } else {
                        // 
                        biT.InitFromBigint(biDen);
                        biT.Subtract(biHi);
                        w2 = biNum.CompareTo(biT);
                    }

                    // if (biNum + biHi == biDen && even)
                    if (0 == w2 && 0 == (dblLo & 1)) {
                        // Rounding up this digit produces exactly (biNum + biHi) which
                        // StrToDbl will round down to dbl.
                        if (bT == 9) {
                            goto LRoundUp9;
                        }
                        if (w1 > 0) {
                            bT++;
                        }
                        mantissa[ib++] = bT;
                        break;
                    }

                    // if (biNum < biHiLo || biNum == biHiLo && even)
                    if (w1 < 0 || 0 == w1 && 0 == (dblLo & 1)) {
                        // if (biNum + biHi > biDen)
                        if (w2 > 0) {
                            // Decide whether to round up.
                            biNum.ShiftLeft(1);
                            w2 = biNum.CompareTo(biDen);
                            if ((w2 > 0 || 0 == w2 && (0 != (bT & 1))) && bT++ == 9) {
                                goto LRoundUp9;
                            }
                        }
                        mantissa[ib++] = bT;
                        break;
                    }

                    // if (biNum + biHi > biDen)
                    if (w2 > 0) {
                        // Round up and be done with it.
                        if (bT != 9) {
                            mantissa[ib++] = (byte)(bT + 1);
                            break;
                        }
                        goto LRoundUp9;
                    }

                    // Save the digit.
                    mantissa[ib++] = bT;

            LSkip:
                    biNum.MulAdd(10, 0);
                    biHi.MulAdd(10, 0);
                    if ((object) biHiLo != (object) biHi) {
                        biHiLo.MulAdd(10, 0);
                    }
                    continue;

            LRoundUp9:
                    while (ib > 0) {
                        if (mantissa[--ib] != 9) {
                            mantissa[ib++]++;
                            goto LReturn;
                        }
                    }
                    wExp10++;
                    mantissa[ib++] = 1;
                    break;
                }

            LReturn:
                exponent = wExp10 + 1;
                mantissaSize = ib;
            }