Exemple #1
0
            // DecAddSub adds or subtracts two decimal values.  On return, d1 contains the result
            // of the operation.  Passing in true for bSign means subtract and false means add.
            // 
            // Returns true if we overflow otherwise false.
            private static bool DecAddSub(ref Decimal d1, ref Decimal d2, bool bSign)
            {
                uint[] rgulNum = new uint[6];
                uint ulPwr;
                int iScale;
                int iHiProd;
                int iCur;
                Split64 sdlTmp = new Split64();
                Decimal result = new Decimal();
                Decimal tmp = new Decimal();

                bSign ^= d2.Sign ^ d1.Sign;

                if (d2.Scale == d1.Scale)
                {
                    // Scale factors are equal, no alignment necessary.
                    //
                    result.Sign = d1.Sign;
                    result.Scale = d1.Scale;

                    if (AlignedAdd(ref result, ref d1, ref d2, bSign))
                        return true;
                }
                else
                {
                    // Scale factors are not equal.  Assume that a larger scale
                    // factor (more decimal places) is likely to mean that number
                    // is smaller.  Start by guessing that the right operand has
                    // the larger scale factor.  The result will have the larger
                    // scale factor.
                    //
                    result.Scale = d2.Scale;  // scale factor of "smaller"
                    result.Sign = d1.Sign;    // but sign of "larger"
                    iScale = result.Scale - d1.Scale;

                    if (iScale < 0)
                    {
                        // Guessed scale factor wrong. Swap operands.
                        //
                        iScale = -iScale;
                        result.Scale = d1.Scale;
                        result.Sign ^= bSign;
                        tmp = d2;
                        d2 = d1;
                        d1 = tmp;
                    }

                    // d1 will need to be multiplied by 10^iScale so
                    // it will have the same scale as d2.  We could be
                    // extending it to up to 192 bits of precision.
                    //
                    if (iScale <= MaxInt32Scale)
                    {
                        // Scaling won't make it larger than 4 ULONGs
                        //
                        ulPwr = s_powers10[iScale];
                        tmp = UInt32x32To64(d1.Low, ulPwr);
                        sdlTmp.int64 = UInt32x32To64(d1.Mid, ulPwr);
                        sdlTmp.int64 += tmp.Mid;
                        tmp.Mid = sdlTmp.Low32;
                        tmp.High = sdlTmp.High32;
                        sdlTmp.int64 = UInt32x32To64(d1.High, ulPwr);
                        sdlTmp.int64 += tmp.High;
                        if (sdlTmp.High32 == 0)
                        {
                            // Result fits in 96 bits.  Use standard aligned add.
                            //
                            tmp.High = sdlTmp.Low32;
                            d1 = tmp;
                            if (AlignedAdd(ref result, ref d1, ref d2, bSign))
                                return true;
                            d1 = result;
                            return false;
                        }
                        rgulNum[0] = tmp.Low;
                        rgulNum[1] = tmp.Mid;
                        rgulNum[2] = sdlTmp.Low32;
                        rgulNum[3] = sdlTmp.High32;
                        iHiProd = 3;
                    }
                    else
                    {
                        // Have to scale by a bunch.  Move the number to a buffer
                        // where it has room to grow as it's scaled.
                        //
                        rgulNum[0] = d1.Low;
                        rgulNum[1] = d1.Mid;
                        rgulNum[2] = d1.High;
                        iHiProd = 2;

                        // Scan for zeros in the upper words.
                        //
                        if (rgulNum[2] == 0)
                        {
                            iHiProd = 1;
                            if (rgulNum[1] == 0)
                            {
                                iHiProd = 0;
                                if (rgulNum[0] == 0)
                                {
                                    // Left arg is zero, return right.
                                    //
                                    result.Low64 = d2.Low64;
                                    result.High = d2.High;
                                    result.Sign ^= bSign;
                                    d1 = result;
                                    return false;
                                }
                            }
                        }

                        // Scaling loop, up to 10^9 at a time.  iHiProd stays updated
                        // with index of highest non-zero ULONG.
                        //
                        for (; iScale > 0; iScale -= MaxInt32Scale)
                        {
                            if (iScale > MaxInt32Scale)
                                ulPwr = TenToPowerNine;
                            else
                                ulPwr = s_powers10[iScale];

                            sdlTmp.High32 = 0;
                            for (iCur = 0; iCur <= iHiProd; iCur++)
                            {
                                sdlTmp.int64 = UInt32x32To64(rgulNum[iCur], ulPwr) + sdlTmp.High32;
                                rgulNum[iCur] = sdlTmp.Low32;
                            }

                            if (sdlTmp.High32 != 0)
                                // We're extending the result by another ULONG.
                                rgulNum[++iHiProd] = sdlTmp.High32;
                        }
                    }

                    // Scaling complete, do the add.  Could be subtract if signs differ.
                    //
                    sdlTmp.Low32 = rgulNum[0];
                    sdlTmp.High32 = rgulNum[1];

                    if (bSign)
                    {
                        // Signs differ, subtract.
                        //
                        result.Low64 = sdlTmp.int64 - d2.Low64;
                        result.High = rgulNum[2] - d2.High;

                        // Propagate carry
                        //
                        if (result.Low64 > sdlTmp.int64)
                        {
                            result.High--;
                            if (result.High >= rgulNum[2])
                                if (LongSub(ref result, ref iHiProd, rgulNum))
                                {
                                    d1 = result;
                                    return false;
                                }
                        }
                        else if (result.High > rgulNum[2])
                        {
                            if (LongSub(ref result, ref iHiProd, rgulNum))
                            {
                                d1 = result;
                                return false;
                            }
                        }
                    }
                    else
                    {
                        // Signs the same, add.
                        //
                        result.Low64 = sdlTmp.int64 + d2.Low64;
                        result.High = rgulNum[2] + d2.High;

                        // Propagate carry
                        //
                        if (result.Low64 < sdlTmp.int64)
                        {
                            result.High++;
                            if (result.High <= rgulNum[2])
                                LongAdd(ref iHiProd, rgulNum);
                        }
                        else if (result.High < rgulNum[2])
                        {
                            LongAdd(ref iHiProd, rgulNum);
                        }
                    }

                    if (iHiProd > 2)
                    {
                        rgulNum[0] = result.Low;
                        rgulNum[1] = result.Mid;
                        rgulNum[2] = result.High;
                        int scale = ScaleResult(rgulNum, iHiProd, result.Scale);
                        if (scale == -1)
                            return true;
                        result.Scale = scale;

                        result.Low = rgulNum[0];
                        result.Mid = rgulNum[1];
                        result.High = rgulNum[2];
                    }
                }

                d1 = result;
                return false;
            }
Exemple #2
0
            /***
            * ScaleResult
            *
            * Entry:
            *   rgulRes - Array of ULONGs with value, least-significant first.
            *   iHiRes  - Index of last non-zero value in rgulRes.
            *   iScale  - Scale factor for this value, range 0 - 2 * DEC_SCALE_MAX
            *
            * Purpose:
            *   See if we need to scale the result to fit it in 96 bits.
            *   Perform needed scaling.  Adjust scale factor accordingly.
            *
            * Exit:
            *   rgulRes updated in place, always 3 ULONGs.
            *   New scale factor returned, -1 if overflow error.
            *
            ***********************************************************************/
            private static int ScaleResult(uint[] rgulRes, int iHiRes, int iScale)
            {
                int iNewScale;
                int iCur;
                uint ulPwr;
                uint ulTmp;
                uint ulSticky;
                Split64 sdlTmp = new Split64();

                // See if we need to scale the result.  The combined scale must
                // be <= DEC_SCALE_MAX and the upper 96 bits must be zero.
                // 
                // Start by figuring a lower bound on the scaling needed to make
                // the upper 96 bits zero.  iHiRes is the index into rgulRes[]
                // of the highest non-zero ULONG.
                // 
                iNewScale = iHiRes * 32 - 64 - 1;
                if (iNewScale > 0)
                {
                    // Find the MSB.
                    //
                    ulTmp = rgulRes[iHiRes];
                    if ((ulTmp & 0xFFFF0000) == 0)
                    {
                        iNewScale -= 16;
                        ulTmp <<= 16;
                    }
                    if ((ulTmp & 0xFF000000) == 0)
                    {
                        iNewScale -= 8;
                        ulTmp <<= 8;
                    }
                    if ((ulTmp & 0xF0000000) == 0)
                    {
                        iNewScale -= 4;
                        ulTmp <<= 4;
                    }
                    if ((ulTmp & 0xC0000000) == 0)
                    {
                        iNewScale -= 2;
                        ulTmp <<= 2;
                    }
                    if ((ulTmp & 0x80000000) == 0)
                    {
                        iNewScale--;
                        ulTmp <<= 1;
                    }

                    // Multiply bit position by log10(2) to figure it's power of 10.
                    // We scale the log by 256.  log(2) = .30103, * 256 = 77.  Doing this 
                    // with a multiply saves a 96-byte lookup table.  The power returned
                    // is <= the power of the number, so we must add one power of 10
                    // to make it's integer part zero after dividing by 256.
                    // 
                    // Note: the result of this multiplication by an approximation of
                    // log10(2) have been exhaustively checked to verify it gives the 
                    // correct result.  (There were only 95 to check...)
                    // 
                    iNewScale = ((iNewScale * 77) >> 8) + 1;

                    // iNewScale = min scale factor to make high 96 bits zero, 0 - 29.
                    // This reduces the scale factor of the result.  If it exceeds the
                    // current scale of the result, we'll overflow.
                    // 
                    if (iNewScale > iScale)
                        return -1;
                }
                else
                    iNewScale = 0;

                // Make sure we scale by enough to bring the current scale factor
                // into valid range.
                //
                if (iNewScale < iScale - DEC_SCALE_MAX)
                    iNewScale = iScale - DEC_SCALE_MAX;

                if (iNewScale != 0)
                {
                    // Scale by the power of 10 given by iNewScale.  Note that this is 
                    // NOT guaranteed to bring the number within 96 bits -- it could 
                    // be 1 power of 10 short.
                    //
                    iScale -= iNewScale;
                    ulSticky = 0;
                    sdlTmp.High32 = 0; // initialize remainder

                    for (;;)
                    {
                        ulSticky |= sdlTmp.High32; // record remainder as sticky bit

                        if (iNewScale > MaxInt32Scale)
                            ulPwr = TenToPowerNine;
                        else
                            ulPwr = s_powers10[iNewScale];

                        // Compute first quotient.
                        // DivMod64by32 returns quotient in Lo, remainder in Hi.
                        //
                        sdlTmp.int64 = DivMod64by32(rgulRes[iHiRes], ulPwr);
                        rgulRes[iHiRes] = sdlTmp.Low32;
                        iCur = iHiRes - 1;

                        if (iCur >= 0)
                        {
                            // If first quotient was 0, update iHiRes.
                            //
                            if (sdlTmp.Low32 == 0)
                                iHiRes--;

                            // Compute subsequent quotients.
                            //
                            do
                            {
                                sdlTmp.Low32 = rgulRes[iCur];
                                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulPwr);
                                rgulRes[iCur] = sdlTmp.Low32;
                                iCur--;
                            } while (iCur >= 0);
                        }

                        iNewScale -= MaxInt32Scale;
                        if (iNewScale > 0)
                            continue; // scale some more

                        // If we scaled enough, iHiRes would be 2 or less.  If not,
                        // divide by 10 more.
                        //
                        if (iHiRes > 2)
                        {
                            iNewScale = 1;
                            iScale--;
                            continue; // scale by 10
                        }

                        // Round final result.  See if remainder >= 1/2 of divisor.
                        // If remainder == 1/2 divisor, round up if odd or sticky bit set.
                        //
                        ulPwr >>= 1;  // power of 10 always even
                        if (ulPwr <= sdlTmp.High32 && (ulPwr < sdlTmp.High32 ||
                                                        ((rgulRes[0] & 1) | ulSticky) != 0))
                        {
                            iCur = -1;
                            while (++rgulRes[++iCur] == 0)
                                ;

                            if (iCur > 2)
                            {
                                // The rounding caused us to carry beyond 96 bits. 
                                // Scale by 10 more.
                                //
                                iHiRes = iCur;
                                ulSticky = 0;  // no sticky bit
                                sdlTmp.High32 = 0; // or remainder
                                iNewScale = 1;
                                iScale--;
                                continue; // scale by 10
                            }
                        }

                        // We may have scaled it more than we planned.  Make sure the scale 
                        // factor hasn't gone negative, indicating overflow.
                        // 
                        if (iScale < 0)
                            return -1;

                        return iScale;
                    } // for(;;)
                }
                return iScale;
            }
Exemple #3
0
            // Adjust the quotient to deal with an overflow. We need to divide by 10, 
            // feed in the high bit to undo the overflow and then round as required, 
            private static void OverflowUnscale(uint[] rgulQuo, bool fRemainder)
            {
                Split64 sdlTmp = new Split64();

                // We have overflown, so load the high bit with a one.
                sdlTmp.High32 = 1;
                sdlTmp.Low32 = rgulQuo[2];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                rgulQuo[2] = sdlTmp.Low32;
                sdlTmp.Low32 = rgulQuo[1];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                rgulQuo[1] = sdlTmp.Low32;
                sdlTmp.Low32 = rgulQuo[0];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                rgulQuo[0] = sdlTmp.Low32;
                // The remainder is the last digit that does not fit, so we can use it to work out if we need to round up
                if ((sdlTmp.High32 > 5) || ((sdlTmp.High32 == 5) && (fRemainder || (rgulQuo[0] & 1) != 0)))
                    Add32To96(rgulQuo, 1);
            }
Exemple #4
0
            /***
             * Div128By96
             *
             * Entry:
             *   rgulNum - Pointer to 128-bit dividend as array of ULONGs, least-sig first
             *   rgulDen - Pointer to 96-bit divisor.
             *
             * Purpose:
             *   Do partial divide, yielding 32-bit result and 96-bit remainder.
             *   Top divisor ULONG must be larger than top dividend ULONG.  This is
             *   assured in the initial call because the divisor is normalized
             *   and the dividend can't be.  In subsequent calls, the remainder
             *   is multiplied by 10^9 (max), so it can be no more than 1/4 of
             *   the divisor which is effectively multiplied by 2^32 (4 * 10^9).
             *
             * Exit:
             *   Remainder overwrites lower 96-bits of dividend.
             *   Returns quotient.
             *
             * Exceptions:
             *   None.
             *
             ***********************************************************************/
            private static uint Div128By96(uint[] rgulNum, uint[] rgulDen)
            {
                Split64 sdlQuo = new Split64();
                Split64 sdlNum = new Split64();
                Split64 sdlProd1 = new Split64();
                Split64 sdlProd2 = new Split64();

                sdlNum.Low32 = rgulNum[0];
                sdlNum.High32 = rgulNum[1];

                if (rgulNum[3] == 0 && rgulNum[2] < rgulDen[2])
                    // Result is zero.  Entire dividend is remainder.
                    //
                    return 0;

                // DivMod64by32 returns quotient in Lo, remainder in Hi.
                //
                sdlQuo.Low32 = rgulNum[2];
                sdlQuo.High32 = rgulNum[3];
                sdlQuo.int64 = DivMod64by32(sdlQuo.int64, rgulDen[2]);

                // Compute full remainder, rem = dividend - (quo * divisor).
                //
                sdlProd1.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[0]); // quo * lo divisor
                sdlProd2.int64 = UInt32x32To64(sdlQuo.Low32, rgulDen[1]); // quo * mid divisor
                sdlProd2.int64 += sdlProd1.High32;
                sdlProd1.High32 = sdlProd2.Low32;

                sdlNum.int64 -= sdlProd1.int64;
                rgulNum[2] = sdlQuo.High32 - sdlProd2.High32; // sdlQuo.Hi is remainder

                // Propagate carries
                //
                bool fallthru = false;
                if (sdlNum.int64 > ~sdlProd1.int64)
                {
                    rgulNum[2]--;
                    if (rgulNum[2] >= ~sdlProd2.High32)
                        fallthru = true;
                }
                if (fallthru || rgulNum[2] > ~sdlProd2.High32)
                {
                    // Remainder went negative.  Add divisor back in until it's positive,
                    // a max of 2 times.
                    //
                    sdlProd1.Low32 = rgulDen[0];
                    sdlProd1.High32 = rgulDen[1];

                    for (;;)
                    {
                        sdlQuo.Low32--;
                        sdlNum.int64 += sdlProd1.int64;
                        rgulNum[2] += rgulDen[2];

                        if (sdlNum.int64 < sdlProd1.int64)
                        {
                            // Detected carry. Check for carry out of top
                            // before adding it in.
                            //
                            if (rgulNum[2]++ < rgulDen[2])
                                break;
                        }
                        if (rgulNum[2] < rgulDen[2])
                            break; // detected carry
                    }
                }

                rgulNum[0] = sdlNum.Low32;
                rgulNum[1] = sdlNum.High32;
                return sdlQuo.Low32;
            }
Exemple #5
0
            /***
             * IncreaseScale
             *
             * Entry:
             *   rgulNum - Pointer to 96-bit number as array of ULONGs, least-sig first
             *   ulPwr   - Scale factor to multiply by
             *
             * Purpose:
             *   Multiply the two numbers.  The low 96 bits of the result overwrite
             *   the input.  The last 32 bits of the product are the return value.
             *
             * Exit:
             *   Returns highest 32 bits of product.
             *
             * Exceptions:
             *   None.
             *
             ***********************************************************************/
            private static uint IncreaseScale(uint[] rgulNum, uint ulPwr)
            {
                Split64 sdlTmp = new Split64();

                sdlTmp.int64 = UInt32x32To64(rgulNum[0], ulPwr);
                rgulNum[0] = sdlTmp.Low32;
                sdlTmp.int64 = UInt32x32To64(rgulNum[1], ulPwr) + sdlTmp.High32;
                rgulNum[1] = sdlTmp.Low32;
                sdlTmp.int64 = UInt32x32To64(rgulNum[2], ulPwr) + sdlTmp.High32;
                rgulNum[2] = sdlTmp.Low32;
                return sdlTmp.High32;
            }
Exemple #6
0
            /***
             * Div96By32
             *
             * Entry:
             *   rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
             *   ulDen   - 32-bit divisor.
             *
             * Purpose:
             *   Do full divide, yielding 96-bit result and 32-bit remainder.
             *
             * Exit:
             *   Quotient overwrites dividend.
             *   Returns remainder.
             *
             * Exceptions:
             *   None.
             *
             ***********************************************************************/
            private static uint Div96By32(uint[] rgulNum, uint ulDen)
            {
                Split64 sdlTmp = new Split64();

                sdlTmp.High32 = 0;

                if (rgulNum[2] != 0)
                    goto Div3Word;

                if (rgulNum[1] >= ulDen)
                    goto Div2Word;

                sdlTmp.High32 = rgulNum[1];
                rgulNum[1] = 0;
                goto Div1Word;

            Div3Word:
                sdlTmp.Low32 = rgulNum[2];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
                rgulNum[2] = sdlTmp.Low32;
            Div2Word:
                sdlTmp.Low32 = rgulNum[1];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
                rgulNum[1] = sdlTmp.Low32;
            Div1Word:
                sdlTmp.Low32 = rgulNum[0];
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
                rgulNum[0] = sdlTmp.Low32;

                return sdlTmp.High32;
            }
Exemple #7
0
            /***
             * Div96By64
             *
             * Entry:
             *   rgulNum - Pointer to 96-bit dividend as array of ULONGs, least-sig first
             *   sdlDen  - 64-bit divisor.
             *
             * Purpose:
             *   Do partial divide, yielding 32-bit result and 64-bit remainder.
             *   Divisor must be larger than upper 64 bits of dividend.
             *
             * Exit:
             *   Remainder overwrites lower 64-bits of dividend.
             *   Returns quotient.
             *
             * Exceptions:
             *   None.
             *
             ***********************************************************************/
            private static uint Div96By64(System.Collections.Generic.IList<uint> rgulNum, Split64 sdlDen)
            {
                Split64 sdlQuo = new Split64();
                Split64 sdlNum = new Split64();
                Split64 sdlProd = new Split64();

                sdlNum.Low32 = rgulNum[0];

                if (rgulNum[2] >= sdlDen.High32)
                {
                    // Divide would overflow.  Assume a quotient of 2^32, and set
                    // up remainder accordingly.
                    //
                    sdlNum.High32 = rgulNum[1] - sdlDen.Low32;
                    sdlQuo.Low32 = 0;

                    // Remainder went negative.  Add divisor back in until it's positive,
                    // a max of 2 times.
                    //
                    do
                    {
                        sdlQuo.Low32--;
                        sdlNum.int64 += sdlDen.int64;
                    } while (sdlNum.int64 >= sdlDen.int64);

                    goto Done;
                }

                // Hardware divide won't overflow
                //
                if (rgulNum[2] == 0 && rgulNum[1] < sdlDen.High32)
                    // Result is zero.  Entire dividend is remainder.
                    //
                    return 0;

                // DivMod64by32 returns quotient in Lo, remainder in Hi.
                //
                sdlQuo.Low32 = rgulNum[1];
                sdlQuo.High32 = rgulNum[2];
                sdlQuo.int64 = DivMod64by32(sdlQuo.int64, sdlDen.High32);
                sdlNum.High32 = sdlQuo.High32; // remainder

                // Compute full remainder, rem = dividend - (quo * divisor).
                //
                sdlProd.int64 = UInt32x32To64(sdlQuo.Low32, sdlDen.Low32); // quo * lo divisor
                sdlNum.int64 -= sdlProd.int64;

                if (sdlNum.int64 > ~sdlProd.int64)
                {
                    // Remainder went negative.  Add divisor back in until it's positive,
                    // a max of 2 times.
                    //
                    do
                    {
                        sdlQuo.Low32--;
                        sdlNum.int64 += sdlDen.int64;
                    } while (sdlNum.int64 >= sdlDen.int64);
                }

            Done:
                rgulNum[0] = sdlNum.Low32;
                rgulNum[1] = sdlNum.High32;
                return sdlQuo.Low32;
            }
Exemple #8
0
            private static uint FullDiv64By32(ref ulong pdlNum, uint ulDen)
            {
                Split64 sdlTmp = new Split64();
                Split64 sdlRes = new Split64();

                sdlTmp.int64 = pdlNum;
                sdlRes.High32 = 0;

                if (sdlTmp.High32 >= ulDen)
                {
                    // DivMod64by32 returns quotient in Lo, remainder in Hi.
                    //
                    sdlRes.Low32 = sdlTmp.High32;
                    sdlRes.int64 = DivMod64by32(sdlRes.int64, ulDen);
                    sdlTmp.High32 = sdlRes.High32;
                    sdlRes.High32 = sdlRes.Low32;
                }

                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, ulDen);
                sdlRes.Low32 = sdlTmp.Low32;
                pdlNum = sdlRes.int64;
                return sdlTmp.High32;
            }
Exemple #9
0
            private static ulong UInt64x64To128(Split64 sdlOp1, Split64 sdlOp2, out ulong dlHi)
            {
                Split64 sdlTmp1 = new Split64();
                Split64 sdlTmp2 = new Split64();
                Split64 sdlTmp3 = new Split64();

                sdlTmp1.int64 = UInt32x32To64(sdlOp1.Low32, sdlOp2.Low32); // lo partial prod
                sdlTmp2.int64 = UInt32x32To64(sdlOp1.Low32, sdlOp2.High32); // mid 1 partial prod
                sdlTmp1.High32 += sdlTmp2.Low32;
                if (sdlTmp1.High32 < sdlTmp2.Low32)  // test for carry
                    sdlTmp2.High32++;
                sdlTmp3.int64 = UInt32x32To64(sdlOp1.High32, sdlOp2.High32) + sdlTmp2.High32;
                sdlTmp2.int64 = UInt32x32To64(sdlOp1.High32, sdlOp2.Low32);
                sdlTmp1.High32 += sdlTmp2.Low32;
                if (sdlTmp1.High32 < sdlTmp2.Low32)  // test for carry
                    sdlTmp2.High32++;
                sdlTmp3.int64 += sdlTmp2.High32;

                dlHi = sdlTmp3.int64;
                return sdlTmp1.int64;
            }
Exemple #10
0
            private static ulong DivMod32by32(uint num, uint den)
            {
                Split64 sdl = new Split64();

                sdl.Low32 = num / den;
                sdl.High32 = num % den;
                return sdl.int64;
            }
Exemple #11
0
            // VarDecDiv divides two decimal values.  On return, d1 contains the result
            // of the operation.
            internal static void VarDecDiv(ref Decimal d1, ref Decimal d2)
            {
                uint[] rgulQuo = new uint[3];
                uint[] rgulQuoSave = new uint[3];
                uint[] rgulRem = new uint[4];
                uint[] rgulDivisor = new uint[3];
                uint ulPwr;
                uint ulTmp;
                uint ulTmp1;
                Split64 sdlTmp = new Split64();
                Split64 sdlDivisor = new Split64();
                int iScale;
                int iCurScale;
                bool fUnscale;

                iScale = d1.Scale - d2.Scale;
                fUnscale = false;
                rgulDivisor[0] = d2.Low;
                rgulDivisor[1] = d2.Mid;
                rgulDivisor[2] = d2.High;

                if (rgulDivisor[1] == 0 && rgulDivisor[2] == 0)
                {
                    // Divisor is only 32 bits.  Easy divide.
                    //
                    if (rgulDivisor[0] == 0)
                        throw new DivideByZeroException(SR.Overflow_Decimal);

                    rgulQuo[0] = d1.Low;
                    rgulQuo[1] = d1.Mid;
                    rgulQuo[2] = d1.High;
                    rgulRem[0] = Div96By32(rgulQuo, rgulDivisor[0]);

                    for (;;)
                    {
                        if (rgulRem[0] == 0)
                        {
                            if (iScale < 0)
                            {
                                iCurScale = Math.Min(9, -iScale);
                                goto HaveScale;
                            }
                            break;
                        }

                        // We need to unscale if and only if we have a non-zero remainder
                        fUnscale = true;

                        // We have computed a quotient based on the natural scale 
                        // ( <dividend scale> - <divisor scale> ).  We have a non-zero 
                        // remainder, so now we should increase the scale if possible to 
                        // include more quotient bits.
                        // 
                        // If it doesn't cause overflow, we'll loop scaling by 10^9 and 
                        // computing more quotient bits as long as the remainder stays 
                        // non-zero.  If scaling by that much would cause overflow, we'll 
                        // drop out of the loop and scale by as much as we can.
                        // 
                        // Scaling by 10^9 will overflow if rgulQuo[2].rgulQuo[1] >= 2^32 / 10^9 
                        // = 4.294 967 296.  So the upper limit is rgulQuo[2] == 4 and 
                        // rgulQuo[1] == 0.294 967 296 * 2^32 = 1,266,874,889.7+.  Since 
                        // quotient bits in rgulQuo[0] could be all 1's, then 1,266,874,888 
                        // is the largest value in rgulQuo[1] (when rgulQuo[2] == 4) that is 
                        // assured not to overflow.
                        // 
                        iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale);
                        if (iCurScale == 0)
                        {
                            // No more scaling to be done, but remainder is non-zero.
                            // Round quotient.
                            //
                            ulTmp = rgulRem[0] << 1;
                            if (ulTmp < rgulRem[0] || (ulTmp >= rgulDivisor[0] &&
                                                       (ulTmp > rgulDivisor[0] || (rgulQuo[0] & 1) != 0)))
                                RoundUp(rgulQuo, ref iScale);
                            break;
                        }

                        if (iCurScale < 0)
                            throw new OverflowException(SR.Overflow_Decimal);

                        HaveScale:
                        ulPwr = s_powers10[iCurScale];
                        iScale += iCurScale;

                        if (IncreaseScale(rgulQuo, ulPwr) != 0)
                            throw new OverflowException(SR.Overflow_Decimal);

                        sdlTmp.int64 = DivMod64by32(UInt32x32To64(rgulRem[0], ulPwr), rgulDivisor[0]);
                        rgulRem[0] = sdlTmp.High32;

                        if (!Add32To96(rgulQuo, sdlTmp.Low32))
                        {
                            if (iScale == 0)
                                throw new OverflowException(SR.Overflow_Decimal);
                            iScale--;
                            OverflowUnscale(rgulQuo, (rgulRem[0] != 0));
                            break;
                        }
                    } // for (;;)
                }
                else
                {
                    // Divisor has bits set in the upper 64 bits.
                    //
                    // Divisor must be fully normalized (shifted so bit 31 of the most 
                    // significant ULONG is 1).  Locate the MSB so we know how much to 
                    // normalize by.  The dividend will be shifted by the same amount so 
                    // the quotient is not changed.
                    //
                    if (rgulDivisor[2] == 0)
                        ulTmp = rgulDivisor[1];
                    else
                        ulTmp = rgulDivisor[2];

                    iCurScale = 0;
                    if ((ulTmp & 0xFFFF0000) == 0)
                    {
                        iCurScale += 16;
                        ulTmp <<= 16;
                    }
                    if ((ulTmp & 0xFF000000) == 0)
                    {
                        iCurScale += 8;
                        ulTmp <<= 8;
                    }
                    if ((ulTmp & 0xF0000000) == 0)
                    {
                        iCurScale += 4;
                        ulTmp <<= 4;
                    }
                    if ((ulTmp & 0xC0000000) == 0)
                    {
                        iCurScale += 2;
                        ulTmp <<= 2;
                    }
                    if ((ulTmp & 0x80000000) == 0)
                    {
                        iCurScale++;
                        ulTmp <<= 1;
                    }

                    // Shift both dividend and divisor left by iCurScale.
                    // 
                    sdlTmp.int64 = d1.Low64 << iCurScale;
                    rgulRem[0] = sdlTmp.Low32;
                    rgulRem[1] = sdlTmp.High32;
                    sdlTmp.Low32 = d1.Mid;
                    sdlTmp.High32 = d1.High;
                    sdlTmp.int64 <<= iCurScale;
                    rgulRem[2] = sdlTmp.High32;
                    rgulRem[3] = (d1.High >> (31 - iCurScale)) >> 1;

                    sdlDivisor.Low32 = rgulDivisor[0];
                    sdlDivisor.High32 = rgulDivisor[1];
                    sdlDivisor.int64 <<= iCurScale;

                    if (rgulDivisor[2] == 0)
                    {
                        // Have a 64-bit divisor in sdlDivisor.  The remainder 
                        // (currently 96 bits spread over 4 ULONGs) will be < divisor.
                        // 
                        sdlTmp.Low32 = rgulRem[2];
                        sdlTmp.High32 = rgulRem[3];

                        rgulQuo[2] = 0;
                        rgulQuo[1] = Div96By64(new ArraySegment<uint>(rgulRem, 1, 3), sdlDivisor);
                        rgulQuo[0] = Div96By64(rgulRem, sdlDivisor);

                        for (;;)
                        {
                            if ((rgulRem[0] | rgulRem[1]) == 0)
                            {
                                if (iScale < 0)
                                {
                                    iCurScale = Math.Min(9, -iScale);
                                    goto HaveScale64;
                                }
                                break;
                            }

                            // We need to unscale if and only if we have a non-zero remainder
                            fUnscale = true;

                            // Remainder is non-zero.  Scale up quotient and remainder by 
                            // powers of 10 so we can compute more significant bits.
                            // 
                            iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale);
                            if (iCurScale == 0)
                            {
                                // No more scaling to be done, but remainder is non-zero.
                                // Round quotient.
                                //
                                sdlTmp.Low32 = rgulRem[0];
                                sdlTmp.High32 = rgulRem[1];
                                if (sdlTmp.High32 >= 0x80000000 || (sdlTmp.int64 <<= 1) > sdlDivisor.int64 ||
                                    (sdlTmp.int64 == sdlDivisor.int64 && (rgulQuo[0] & 1) != 0))
                                    RoundUp(rgulQuo, ref iScale);
                                break;
                            }

                            if (iCurScale < 0)
                                throw new OverflowException(SR.Overflow_Decimal);

                            HaveScale64:
                            ulPwr = s_powers10[iCurScale];
                            iScale += iCurScale;

                            if (IncreaseScale(rgulQuo, ulPwr) != 0)
                                throw new OverflowException(SR.Overflow_Decimal);

                            rgulRem[2] = 0;  // rem is 64 bits, IncreaseScale uses 96
                            IncreaseScale(rgulRem, ulPwr);
                            ulTmp = Div96By64(rgulRem, sdlDivisor);
                            if (!Add32To96(rgulQuo, ulTmp))
                            {
                                if (iScale == 0)
                                    throw new OverflowException(SR.Overflow_Decimal);
                                iScale--;
                                OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0));
                                break;
                            }
                        } // for (;;)
                    }
                    else
                    {
                        // Have a 96-bit divisor in rgulDivisor[].
                        //
                        // Start by finishing the shift left by iCurScale.
                        //
                        sdlTmp.Low32 = rgulDivisor[1];
                        sdlTmp.High32 = rgulDivisor[2];
                        sdlTmp.int64 <<= iCurScale;
                        rgulDivisor[0] = sdlDivisor.Low32;
                        rgulDivisor[1] = sdlDivisor.High32;
                        rgulDivisor[2] = sdlTmp.High32;

                        // The remainder (currently 96 bits spread over 4 ULONGs) 
                        // will be < divisor.
                        //
                        rgulQuo[2] = 0;
                        rgulQuo[1] = 0;
                        rgulQuo[0] = Div128By96(rgulRem, rgulDivisor);

                        for (;;)
                        {
                            if ((rgulRem[0] | rgulRem[1] | rgulRem[2]) == 0)
                            {
                                if (iScale < 0)
                                {
                                    iCurScale = Math.Min(9, -iScale);
                                    goto HaveScale96;
                                }
                                break;
                            }

                            // We need to unscale if and only if we have a non-zero remainder
                            fUnscale = true;

                            // Remainder is non-zero.  Scale up quotient and remainder by 
                            // powers of 10 so we can compute more significant bits.
                            //
                            iCurScale = SearchScale(rgulQuo[2], rgulQuo[1], rgulQuo[0], iScale);
                            if (iCurScale == 0)
                            {
                                // No more scaling to be done, but remainder is non-zero.
                                // Round quotient.
                                //
                                if (rgulRem[2] >= 0x80000000)
                                {
                                    RoundUp(rgulQuo, ref iScale);
                                    break;
                                }

                                ulTmp = (rgulRem[0] > 0x80000000) ? 1u : 0u;
                                ulTmp1 = (rgulRem[1] > 0x80000000) ? 1u : 0u;
                                rgulRem[0] <<= 1;
                                rgulRem[1] = (rgulRem[1] << 1) + ulTmp;
                                rgulRem[2] = (rgulRem[2] << 1) + ulTmp1;

                                if (rgulRem[2] > rgulDivisor[2] || rgulRem[2] == rgulDivisor[2] &&
                                    (rgulRem[1] > rgulDivisor[1] || rgulRem[1] == rgulDivisor[1] &&
                                     (rgulRem[0] > rgulDivisor[0] || rgulRem[0] == rgulDivisor[0] &&
                                      (rgulQuo[0] & 1) != 0)))
                                    RoundUp(rgulQuo, ref iScale);
                                break;
                            }

                            if (iCurScale < 0)
                                throw new OverflowException(SR.Overflow_Decimal);

                            HaveScale96:
                            ulPwr = s_powers10[iCurScale];
                            iScale += iCurScale;

                            if (IncreaseScale(rgulQuo, ulPwr) != 0)
                                throw new OverflowException(SR.Overflow_Decimal);

                            rgulRem[3] = IncreaseScale(rgulRem, ulPwr);
                            ulTmp = Div128By96(rgulRem, rgulDivisor);
                            if (!Add32To96(rgulQuo, ulTmp))
                            {
                                if (iScale == 0)
                                    throw new OverflowException(SR.Overflow_Decimal);
                                iScale--;
                                OverflowUnscale(rgulQuo, (rgulRem[0] != 0 || rgulRem[1] != 0 || rgulRem[2] != 0 || rgulRem[3] != 0));
                                break;
                            }
                        } // for (;;)
                    }
                }

                // We need to unscale if and only if we have a non-zero remainder
                if (fUnscale)
                {
                    // Try extracting any extra powers of 10 we may have 
                    // added.  We do this by trying to divide out 10^8, 10^4, 10^2, and 10^1.
                    // If a division by one of these powers returns a zero remainder, then
                    // we keep the quotient.  If the remainder is not zero, then we restore
                    // the previous value.
                    // 
                    // Since 10 = 2 * 5, there must be a factor of 2 for every power of 10
                    // we can extract.  We use this as a quick test on whether to try a
                    // given power.
                    // 
                    while ((rgulQuo[0] & 0xFF) == 0 && iScale >= 8)
                    {
                        rgulQuoSave[0] = rgulQuo[0];
                        rgulQuoSave[1] = rgulQuo[1];
                        rgulQuoSave[2] = rgulQuo[2];

                        if (Div96By32(rgulQuoSave, 100000000) == 0)
                        {
                            rgulQuo[0] = rgulQuoSave[0];
                            rgulQuo[1] = rgulQuoSave[1];
                            rgulQuo[2] = rgulQuoSave[2];
                            iScale -= 8;
                        }
                        else
                            break;
                    }

                    if ((rgulQuo[0] & 0xF) == 0 && iScale >= 4)
                    {
                        rgulQuoSave[0] = rgulQuo[0];
                        rgulQuoSave[1] = rgulQuo[1];
                        rgulQuoSave[2] = rgulQuo[2];

                        if (Div96By32(rgulQuoSave, 10000) == 0)
                        {
                            rgulQuo[0] = rgulQuoSave[0];
                            rgulQuo[1] = rgulQuoSave[1];
                            rgulQuo[2] = rgulQuoSave[2];
                            iScale -= 4;
                        }
                    }

                    if ((rgulQuo[0] & 3) == 0 && iScale >= 2)
                    {
                        rgulQuoSave[0] = rgulQuo[0];
                        rgulQuoSave[1] = rgulQuo[1];
                        rgulQuoSave[2] = rgulQuo[2];

                        if (Div96By32(rgulQuoSave, 100) == 0)
                        {
                            rgulQuo[0] = rgulQuoSave[0];
                            rgulQuo[1] = rgulQuoSave[1];
                            rgulQuo[2] = rgulQuoSave[2];
                            iScale -= 2;
                        }
                    }

                    if ((rgulQuo[0] & 1) == 0 && iScale >= 1)
                    {
                        rgulQuoSave[0] = rgulQuo[0];
                        rgulQuoSave[1] = rgulQuo[1];
                        rgulQuoSave[2] = rgulQuo[2];

                        if (Div96By32(rgulQuoSave, 10) == 0)
                        {
                            rgulQuo[0] = rgulQuoSave[0];
                            rgulQuo[1] = rgulQuoSave[1];
                            rgulQuo[2] = rgulQuoSave[2];
                            iScale -= 1;
                        }
                    }
                }

                d1.Sign = d1.Sign ^ d2.Sign;
                d1.High = rgulQuo[2];
                d1.Mid = rgulQuo[1];
                d1.Low = rgulQuo[0];
                d1.Scale = iScale;
            }
Exemple #12
0
            private static ulong DivMod64by32(ulong num, uint den)
            {
                Split64 sdl = new Split64();

                sdl.Low32 = (uint)(num / den);
                sdl.High32 = (uint)(num % den);
                return sdl.int64;
            }
Exemple #13
0
            //**********************************************************************
            // VarDecFromR8 - Convert double to Decimal
            //**********************************************************************
            internal static void VarDecFromR8(double input, out Decimal pdecOut)
            {
                int iExp;    // number of bits to left of binary point
                int iPower;  // power-of-10 scale factor
                Split64 sdlMant = new Split64();
                Split64 sdlLo = new Split64();
                double dbl;
                int lmax, cur;  // temps used during scale reduction
                uint ulPwrCur;
                uint ulQuo;

                pdecOut = new Decimal();

                // The most we can scale by is 10^28, which is just slightly more
                // than 2^93.  So a float with an exponent of -94 could just
                // barely reach 0.5, but smaller exponents will always round to zero.
                //
                iExp = (int)(GetExponent(input) - DBLBIAS);
                if (iExp < -94)
                    return;  // result should be zeroed out

                if (iExp > 96)
                    throw new OverflowException(SR.Overflow_Decimal);
                dbl = input;
                if (dbl < 0)
                    dbl *= -1;

                // Round the input to a 15-digit integer.  The R8 format has
                // only 15 digits of precision, and we want to keep garbage digits
                // out of the Decimal were making.
                //
                // Calculate max power of 10 input value could have by multiplying 
                // the exponent by log10(2).  Using scaled integer multiplcation, 
                // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
                //

                iPower = 14 - ((iExp * 19728) >> 16);

                if (iPower >= 0)
                {
                    // We have less than 15 digits, scale input up.
                    //
                    if (iPower > DEC_SCALE_MAX)
                        iPower = DEC_SCALE_MAX;

                    dbl = dbl * s_doublePowers10[iPower];
                }
                else
                {
                    if (iPower != -1 || dbl >= 1E15)
                        dbl = dbl / GetDoublePower10(-iPower);
                    else
                        iPower = 0; // didn't scale it
                }

                System.Diagnostics.Debug.Assert(dbl < 1E15);
                if (dbl < 1E14 && iPower < DEC_SCALE_MAX)
                {
                    dbl *= 10;
                    iPower++;
                    System.Diagnostics.Debug.Assert(dbl >= 1E14);
                }

                // Round to int64
                //
                sdlMant.int64 = (ulong)dbl;
                dbl -= (double)sdlMant.int64;  // dif between input & integer
                if (dbl > 0.5 || dbl == 0.5 && (sdlMant.Low32 & 1) != 0)
                    sdlMant.int64++;

                if (sdlMant.int64 == 0)
                    return;  // result should be zeroed out

                if (iPower < 0)
                {
                    // Add -iPower factors of 10, -iPower <= (29 - 15) = 14.
                    //
                    iPower = -iPower;
                    if (iPower < 10)
                    {
                        sdlLo.int64 = UInt32x32To64(sdlMant.Low32, s_powers10[iPower]);
                        sdlMant.int64 = UInt32x32To64(sdlMant.High32, s_powers10[iPower]);
                        sdlMant.int64 += sdlLo.High32;
                        sdlLo.High32 = sdlMant.Low32;
                        sdlMant.Low32 = sdlMant.High32;
                    }
                    else
                    {
                        // Have a big power of 10.
                        //
                        System.Diagnostics.Debug.Assert(iPower <= 14);
                        ulong tmpValue;
                        sdlLo.int64 = UInt64x64To128(sdlMant, new Split64((ulong)s_doublePowers10[iPower]), out tmpValue);
                        sdlMant.int64 = tmpValue;

                        if (sdlMant.High32 != 0)
                            throw new OverflowException(SR.Overflow_Decimal);
                    }
                    pdecOut.Low64 = sdlLo.int64;
                    pdecOut.High = sdlMant.Low32;
                    pdecOut.Scale = 0;
                }
                else
                {
                    // Factor out powers of 10 to reduce the scale, if possible.
                    // The maximum number we could factor out would be 14.  This
                    // comes from the fact we have a 15-digit number, and the 
                    // MSD must be non-zero -- but the lower 14 digits could be 
                    // zero.  Note also the scale factor is never negative, so
                    // we can't scale by any more than the power we used to
                    // get the integer.
                    //
                    // DivMod64by32 returns the quotient in Lo, the remainder in Hi.
                    //
                    lmax = iPower < 14 ? iPower : 14;

                    // lmax is the largest power of 10 to try, lmax <= 14.
                    // We'll try powers 8, 4, 2, and 1 unless they're too big.
                    //
                    for (cur = 8; cur > 0; cur >>= 1)
                    {
                        if (cur > lmax)
                            continue;

                        ulPwrCur = s_powers10[cur];

                        if (sdlMant.High32 >= ulPwrCur)
                        {
                            // Overflow if we try to divide in one step.
                            //
                            sdlLo.int64 = DivMod64by32(sdlMant.High32, ulPwrCur);
                            ulQuo = sdlLo.Low32;
                            sdlLo.Low32 = sdlMant.Low32;
                            sdlLo.int64 = DivMod64by32(sdlLo.int64, ulPwrCur);
                        }
                        else
                        {
                            ulQuo = 0;
                            sdlLo.int64 = DivMod64by32(sdlMant.int64, ulPwrCur);
                        }

                        if (sdlLo.High32 == 0)
                        {
                            sdlMant.High32 = ulQuo;
                            sdlMant.Low32 = sdlLo.Low32;
                            iPower -= cur;
                            lmax -= cur;
                        }
                    }

                    pdecOut.High = 0;
                    pdecOut.Scale = iPower;
                    pdecOut.Low64 = sdlMant.int64;
                }

                pdecOut.Sign = input < 0;
            }
Exemple #14
0
            //**********************************************************************
            // VarDecFromR4 - Convert float to Decimal
            //**********************************************************************
            internal static void VarDecFromR4(float input, out Decimal pdecOut)
            {
                int iExp;    // number of bits to left of binary point
                int iPower;
                uint ulMant;
                double dbl;
                Split64 sdlLo = new Split64();
                Split64 sdlHi = new Split64();
                int lmax, cur;  // temps used during scale reduction

                pdecOut = new Decimal();

                // The most we can scale by is 10^28, which is just slightly more
                // than 2^93.  So a float with an exponent of -94 could just
                // barely reach 0.5, but smaller exponents will always round to zero.
                //
                iExp = (int)(GetExponent(input) - SNGBIAS);
                if (iExp < -94)
                    return; // result should be zeroed out

                if (iExp > 96)
                    throw new OverflowException(SR.Overflow_Decimal);

                // Round the input to a 7-digit integer.  The R4 format has
                // only 7 digits of precision, and we want to keep garbage digits
                // out of the Decimal were making.
                //
                // Calculate max power of 10 input value could have by multiplying 
                // the exponent by log10(2).  Using scaled integer multiplcation, 
                // log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
                //
                dbl = input;
                if (dbl < 0)
                    dbl *= -1;
                iPower = 6 - ((iExp * 19728) >> 16);

                if (iPower >= 0)
                {
                    // We have less than 7 digits, scale input up.
                    //
                    if (iPower > DEC_SCALE_MAX)
                        iPower = DEC_SCALE_MAX;

                    dbl = dbl * s_doublePowers10[iPower];
                }
                else
                {
                    if (iPower != -1 || dbl >= 1E7)
                        dbl = dbl / GetDoublePower10(-iPower);
                    else
                        iPower = 0; // didn't scale it
                }

                System.Diagnostics.Debug.Assert(dbl < 1E7);
                if (dbl < 1E6 && iPower < DEC_SCALE_MAX)
                {
                    dbl *= 10;
                    iPower++;
                    System.Diagnostics.Debug.Assert(dbl >= 1E6);
                }

                // Round to integer
                //
                ulMant = (uint)dbl;
                dbl -= (double)ulMant;  // difference between input & integer
                if (dbl > 0.5 || (dbl == 0.5) && (ulMant & 1) != 0)
                    ulMant++;

                if (ulMant == 0)
                    return;  // result should be zeroed out

                if (iPower < 0)
                {
                    // Add -iPower factors of 10, -iPower <= (29 - 7) = 22.
                    //
                    iPower = -iPower;
                    if (iPower < 10)
                    {
                        pdecOut.Low64 = UInt32x32To64(ulMant, s_powers10[iPower]);
                        pdecOut.High = 0;
                    }
                    else
                    {
                        // Have a big power of 10.
                        //
                        if (iPower > 18)
                        {
                            sdlLo.int64 = UInt32x32To64(ulMant, s_powers10[iPower - 18]);
                            ulong tmplong;
                            sdlLo.int64 = UInt64x64To128(sdlLo, s_tenToPowerEighteen, out tmplong);
                            sdlHi.int64 = tmplong;

                            if (sdlHi.High32 != 0)
                                throw new OverflowException(SR.Overflow_Decimal);
                        }
                        else
                        {
                            sdlLo.int64 = UInt32x32To64(ulMant, s_powers10[iPower - 9]);
                            sdlHi.int64 = UInt32x32To64(TenToPowerNine, sdlLo.High32);
                            sdlLo.int64 = UInt32x32To64(TenToPowerNine, sdlLo.Low32);
                            sdlHi.int64 += sdlLo.High32;
                            sdlLo.High32 = sdlHi.Low32;
                            sdlHi.Low32 = sdlHi.High32;
                        }
                        pdecOut.Low64 = sdlLo.int64;
                        pdecOut.High = sdlHi.Low32;
                    }
                    pdecOut.Scale = 0;
                }
                else
                {
                    // Factor out powers of 10 to reduce the scale, if possible.
                    // The maximum number we could factor out would be 6.  This
                    // comes from the fact we have a 7-digit number, and the 
                    // MSD must be non-zero -- but the lower 6 digits could be 
                    // zero.  Note also the scale factor is never negative, so
                    // we can't scale by any more than the power we used to
                    // get the integer.
                    //
                    // DivMod32by32 returns the quotient in Lo, the remainder in Hi.
                    //
                    lmax = iPower < 6 ? iPower : 6;

                    // lmax is the largest power of 10 to try, lmax <= 6.
                    // We'll try powers 4, 2, and 1 unless they're too big.
                    //
                    for (cur = 4; cur > 0; cur >>= 1)
                    {
                        if (cur > lmax)
                            continue;

                        sdlLo.int64 = DivMod32by32(ulMant, s_powers10[cur]);

                        if (sdlLo.High32 == 0)
                        {
                            ulMant = sdlLo.Low32;
                            iPower -= cur;
                            lmax -= cur;
                        }
                    }
                    pdecOut.Low = ulMant;
                    pdecOut.Mid = 0;
                    pdecOut.High = 0;
                    pdecOut.Scale = iPower;
                }

                pdecOut.Sign = input < 0;
            }
Exemple #15
0
            //**********************************************************************
            // VarDecMul - Decimal Multiply
            //**********************************************************************
            internal static void VarDecMul(ref Decimal pdecL, ref Decimal pdecR, out Decimal pdecRes)
            {
                Split64 sdlTmp = new Split64();
                Split64 sdlTmp2 = new Split64();
                Split64 sdlTmp3 = new Split64();
                int iScale;
                int iHiProd;
                uint ulPwr;
                uint ulRemLo;
                uint ulRemHi;
                uint[] rgulProd = new uint[6];

                pdecRes = new Decimal();
                iScale = pdecL.Scale + pdecR.Scale;

                if ((pdecL.High | pdecL.Mid | pdecR.High | pdecR.Mid) == 0)
                {
                    // Upper 64 bits are zero.
                    //
                    sdlTmp.int64 = UInt32x32To64(pdecL.Low, pdecR.Low);
                    if (iScale > DEC_SCALE_MAX)
                    {
                        // Result iScale is too big.  Divide result by power of 10 to reduce it.
                        // If the amount to divide by is > 19 the result is guaranteed
                        // less than 1/2.  [max value in 64 bits = 1.84E19]
                        //
                        iScale -= DEC_SCALE_MAX;
                        if (iScale > 19)
                        {
                            //DECIMAL_SETZERO(*pdecRes);
                            return;
                        }
                        if (iScale > MaxInt32Scale)
                        {
                            // Divide by 1E10 first, to get the power down to a 32-bit quantity.
                            // 1E10 itself doesn't fit in 32 bits, so we'll divide by 2.5E9 now
                            // then multiply the next divisor by 4 (which will be a max of 4E9).
                            // 
                            ulRemLo = FullDiv64By32(ref sdlTmp.int64, TenToPowerTenDiv4);
                            ulPwr = s_powers10[iScale - 10] << 2;
                        }
                        else
                        {
                            ulPwr = s_powers10[iScale];
                            ulRemLo = 0;
                        }

                        // Power to divide by fits in 32 bits.
                        //
                        ulRemHi = FullDiv64By32(ref sdlTmp.int64, ulPwr);

                        // Round result.  See if remainder >= 1/2 of divisor.
                        // Divisor is a power of 10, so it is always even.
                        //
                        ulPwr >>= 1;
                        if (ulRemHi >= ulPwr && (ulRemHi > ulPwr || (ulRemLo | (sdlTmp.Low32 & 1)) > 0))
                            sdlTmp.int64++;

                        iScale = DEC_SCALE_MAX;
                    }
                    pdecRes.Low64 = sdlTmp.int64;
                    pdecRes.High = 0;
                }
                else
                {
                    // At least one operand has bits set in the upper 64 bits.
                    //
                    // Compute and accumulate the 9 partial products into a 
                    // 192-bit (24-byte) result.
                    //
                    //        [l-h][l-m][l-l]      left high, middle, low
                    //         x    [r-h][r-m][r-l]      right high, middle, low
                    // ------------------------------
                    //
                    //             [0-h][0-l]      l-l * r-l
                    //        [1ah][1al]      l-l * r-m
                    //        [1bh][1bl]      l-m * r-l
                    //       [2ah][2al]          l-m * r-m
                    //       [2bh][2bl]          l-l * r-h
                    //       [2ch][2cl]          l-h * r-l
                    //      [3ah][3al]          l-m * r-h
                    //      [3bh][3bl]          l-h * r-m
                    // [4-h][4-l]              l-h * r-h
                    // ------------------------------
                    // [p-5][p-4][p-3][p-2][p-1][p-0]      prod[] array
                    //
                    sdlTmp.int64 = UInt32x32To64(pdecL.Low, pdecR.Low);
                    rgulProd[0] = sdlTmp.Low32;

                    sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.Mid) + sdlTmp.High32;

                    sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Low);
                    sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
                    rgulProd[1] = sdlTmp.Low32;
                    if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
                        sdlTmp2.High32 = 1;
                    else
                        sdlTmp2.High32 = 0;
                    sdlTmp2.Low32 = sdlTmp.High32;

                    sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.Mid) + sdlTmp2.int64;

                    if ((pdecL.High | pdecR.High) > 0)
                    {
                        // Highest 32 bits is non-zero.     Calculate 5 more partial products.
                        //
                        sdlTmp2.int64 = UInt32x32To64(pdecL.Low, pdecR.High);
                        sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
                        if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
                            sdlTmp3.High32 = 1;
                        else
                            sdlTmp3.High32 = 0;

                        sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Low);
                        sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
                        rgulProd[2] = sdlTmp.Low32;
                        if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
                            sdlTmp3.High32++;
                        sdlTmp3.Low32 = sdlTmp.High32;

                        sdlTmp.int64 = UInt32x32To64(pdecL.Mid, pdecR.High);
                        sdlTmp.int64 += sdlTmp3.int64; // this could generate carry
                        if (sdlTmp.int64 < sdlTmp3.int64) // detect carry
                            sdlTmp3.High32 = 1;
                        else
                            sdlTmp3.High32 = 0;

                        sdlTmp2.int64 = UInt32x32To64(pdecL.High, pdecR.Mid);
                        sdlTmp.int64 += sdlTmp2.int64; // this could generate carry
                        rgulProd[3] = sdlTmp.Low32;
                        if (sdlTmp.int64 < sdlTmp2.int64) // detect carry
                            sdlTmp3.High32++;
                        sdlTmp3.Low32 = sdlTmp.High32;

                        sdlTmp.int64 = UInt32x32To64(pdecL.High, pdecR.High) + sdlTmp3.int64;
                        rgulProd[4] = sdlTmp.Low32;
                        rgulProd[5] = sdlTmp.High32;

                        iHiProd = 5;
                    }
                    else
                    {
                        rgulProd[2] = sdlTmp.Low32;
                        rgulProd[3] = sdlTmp.High32;
                        iHiProd = 3;
                    }

                    // Check for leading zero ULONGs on the product
                    //
                    while (rgulProd[iHiProd] == 0)
                    {
                        iHiProd--;
                        if (iHiProd < 0)
                            return;
                    }

                    iScale = ScaleResult(rgulProd, iHiProd, iScale);
                    if (iScale == -1)
                        throw new OverflowException(SR.Overflow_Decimal);

                    pdecRes.Low = rgulProd[0];
                    pdecRes.Mid = rgulProd[1];
                    pdecRes.High = rgulProd[2];
                }

                pdecRes.Sign = pdecR.Sign ^ pdecL.Sign;
                pdecRes.Scale = (char)iScale;
            }
Exemple #16
0
            // Returns true if we overflowed
            private static bool AlignedScale(ref Decimal value)
            {
                Split64 sdlTmp = new Split64();

                // Divide the value by 10, dropping the scale factor.
                // 
                if (value.Scale == 0)
                    return true;
                value.Scale--;

                sdlTmp.Low32 = value.High;
                sdlTmp.High32 = 1;
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                value.High = sdlTmp.Low32;

                sdlTmp.Low32 = value.Mid;
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                value.Mid = sdlTmp.Low32;

                sdlTmp.Low32 = value.Low;
                sdlTmp.int64 = DivMod64by32(sdlTmp.int64, 10);
                value.Low = sdlTmp.Low32;

                // See if we need to round up.
                //
                if (sdlTmp.High32 >= 5 && (sdlTmp.High32 > 5 || (value.Low & 1) != 0))
                {
                    value.Low64 = value.Low64 + 1;
                    if (value.Low64 == 0)
                        value.High++;
                }
                return false;
            }