SQLDecimal.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Data / System / Data / SQLTypes / SQLDecimal.cs / 1 / SQLDecimal.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
//  
// junfang 
// [....]
// [....] 
// [....] 
//-----------------------------------------------------------------------------
 
//*************************************************************************
// @File: SqlNumeric.cs
//
// Create by:    JunFang 
// @Owner:      JunFang
// 
// Purpose: Implementation of SqlMoney which is equivalent to 
//            data type "numeric" and "decimal" in SQL Server
// 
// Notes:
//
// History:
// 
//    @Version: Yukon
//    118776 JXF  09/17/02 Double to SqlDecimal/SqlMoney conversion 
//    102740 BDS  03/28/02 UDT Serialization 
//    86121 AZA  08/21/01 Stop accessing Precision and Scale on null SqlDecimal
//                        objects. 
//
//   09/17/99  JunFang    Created and implemented as first drop.
//
// @EndHeader@ 
//*************************************************************************
 
using System; 
using System.Data.Common;
using System.Diagnostics; 
using System.Globalization;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Schema; 
using System.Xml.Serialization;
 
namespace System.Data.SqlTypes { 

    ///  
    ///    
    ///       Represents a fixed precision and scale numeric value between -10
    ///       -1 and 10 -1 to be stored in or retrieved from a database.
    ///     
    /// 
    [Serializable] 
    [StructLayout(LayoutKind.Sequential)] 
    [XmlSchemaProvider("GetXsdType")]
    public struct SqlDecimal : INullable, IComparable, IXmlSerializable { 
        // data in CSsNumeric in SQL Server
        // BYTE    m_cbLen;                // # of DWORDs + 1 (1 is for sign)
        // BYTE    m_bPrec;                // precision
        // BYTE    m_bScale;                // scale 
        // BYTE    m_bSign;                // NUM_POSITIVE or NUM_NEGATIVE
        // ULONG    m_rgulData [x_culNumeMax]; 
 
        internal byte    m_bStatus;      // bit 0: fNotNull, bit 1: fNegative
        internal byte    m_bLen;         // number of uints used, = (CSsNumeric.m_cbLen - 1) / 4. 
        internal byte    m_bPrec;
        internal byte    m_bScale;
        internal UInt32  m_data1;
        internal UInt32  m_data2; 
        internal UInt32  m_data3;
        internal UInt32  m_data4; 
 
        private static readonly byte NUMERIC_MAX_PRECISION = 38;            // Maximum precision of numeric
        ///  
        ///    [To be supplied.]
        /// 
        public  static readonly byte MaxPrecision = NUMERIC_MAX_PRECISION;  // max SS precision
        ///  
        ///    [To be supplied.]
        ///  
        public  static readonly byte MaxScale = NUMERIC_MAX_PRECISION;      // max SS scale 

        private static readonly byte    x_bNullMask     = 1;    // bit mask for null bit in m_bStatus 
        private static readonly byte    x_bIsNull       = 0;    // is null
        private static readonly byte    x_bNotNull      = 1;    // is not null
        private static readonly byte    x_bReverseNullMask  = unchecked((byte)~x_bNullMask);
 
        private static readonly byte    x_bSignMask     = 2;    // bit mask for sign bit in m_bStatus
        private static readonly byte    x_bPositive     = 0;    // is positive 
        private static readonly byte    x_bNegative     = 2;    // is negative 
        private static readonly byte    x_bReverseSignMask  = unchecked((byte)~x_bSignMask);
 
        private static readonly uint    x_uiZero            = (uint) 0;

        private static readonly int     x_cNumeMax          = 4;
        private static readonly long    x_lInt32Base        = ((long)1) << 32;      // 2**32 
        private static readonly ulong   x_ulInt32Base       = ((ulong)1) << 32;     // 2**32
        private static readonly ulong   x_ulInt32BaseForMod = x_ulInt32Base - 1;    // 2**32 - 1 (0xFFF...FF) 
 
        internal static readonly ulong   x_llMax            = Int64.MaxValue;   // Max of Int64
 
        private static readonly uint    x_ulBase10          = 10;

        private static readonly double  DUINT_BASE  = (double)x_lInt32Base;     // 2**32
        private static readonly double  DUINT_BASE2 = DUINT_BASE * DUINT_BASE;  // 2**64 
        private static readonly double  DUINT_BASE3 = DUINT_BASE2 * DUINT_BASE; // 2**96
        private static readonly double  DMAX_NUME   = 1.0e+38;                  // Max value of numeric 
        private static readonly uint    DBL_DIG     = 17;                       // Max decimal digits of double 

        private static readonly byte    x_cNumeDivScaleMin = 6;     // Minimum result scale of numeric division 

        // Array of multipliers for lAdjust and Ceiling/Floor.
        private static readonly uint[] x_rgulShiftBase = new uint[9] {
            10, 
            10 * 10,
            10 * 10 * 10, 
            10 * 10 * 10 * 10, 
            10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10, 
            10 * 10 * 10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10 * 10 * 10,
            10 * 10 * 10 * 10 * 10 * 10 * 10 * 10 * 10
        }; 

#region DecimalHelperTableGenerator 
/* 
        // the code below will generate the DecimalHelpers tables
        static private string[] HelperNames = { 
            "DecimalHelpersLo", "DecimalHelpersMid", "DecimalHelpersHi", "DecimalHelpersHiHi",
        };

 
        static private void DumpDecimalHelperParts(int index)
        { 
            SqlDecimal sqlDecimalValue = 10;    // start precision=2 

            Console.WriteLine("static private readonly UInt32[] {0} = {{", HelperNames[index]); 
            for (int precision = 2; precision <= SqlDecimal.MaxPrecision; precision++){
                Console.WriteLine("    0x{0,8:x8}, // precision:{1}, value:{2}", sqlDecimalValue.Data[index], precision, sqlDecimalValue.ToString());
                if (precision < SqlDecimal.MaxPrecision){
                    sqlDecimalValue *= 10; 
                }
            } 
            sqlDecimalValue = SqlDecimal.MaxValue; 
            int[] data = sqlDecimalValue.Data;
            UInt32[] udata = { (UInt32)data[0], (UInt32)data[1], (UInt32)data[2], (UInt32)data[3]}; 
            bool carry = true;
            for (int i = 0; i < 4; i++){
                if (carry){
                    carry = (++udata[i] == 0); 
                }
            } 
            Console.WriteLine("    0x{0,8:x8}, // precision:{1}+1, value:{2}+1", udata[index], SqlDecimal.MaxPrecision, SqlDecimal.MaxValue.ToString()); 

            Console.WriteLine("};"); 
            Console.WriteLine();
        }

 
        static public void CreateDecimalHelperTable()
        { 
            for (int i = 0; i < 4; i++) 
            {
                DumpDecimalHelperParts(i); 
            }
        }

*/ 
#endregion
 
#region DecimalHelperTable 
        static private readonly UInt32[] DecimalHelpersLo = {
            0x0000000a, // precision:2, value:10 
            0x00000064, // precision:3, value:100
            0x000003e8, // precision:4, value:1000
            0x00002710, // precision:5, value:10000
            0x000186a0, // precision:6, value:100000 
            0x000f4240, // precision:7, value:1000000
            0x00989680, // precision:8, value:10000000 
            0x05f5e100, // precision:9, value:100000000 
            0x3b9aca00, // precision:10, value:1000000000
            0x540be400, // precision:11, value:10000000000 
            0x4876e800, // precision:12, value:100000000000
            0xd4a51000, // precision:13, value:1000000000000
            0x4e72a000, // precision:14, value:10000000000000
            0x107a4000, // precision:15, value:100000000000000 
            0xa4c68000, // precision:16, value:1000000000000000
            0x6fc10000, // precision:17, value:10000000000000000 
            0x5d8a0000, // precision:18, value:100000000000000000 
            0xa7640000, // precision:19, value:1000000000000000000
            0x89e80000, // precision:20, value:10000000000000000000 
            0x63100000, // precision:21, value:100000000000000000000
            0xdea00000, // precision:22, value:1000000000000000000000
            0xb2400000, // precision:23, value:10000000000000000000000
            0xf6800000, // precision:24, value:100000000000000000000000 
            0xa1000000, // precision:25, value:1000000000000000000000000
            0x4a000000, // precision:26, value:10000000000000000000000000 
            0xe4000000, // precision:27, value:100000000000000000000000000 
            0xe8000000, // precision:28, value:1000000000000000000000000000
            0x10000000, // precision:29, value:10000000000000000000000000000 
            0xa0000000, // precision:30, value:100000000000000000000000000000
            0x40000000, // precision:31, value:1000000000000000000000000000000
            0x80000000, // precision:32, value:10000000000000000000000000000000
            0x00000000, // precision:33, value:100000000000000000000000000000000 
            0x00000000, // precision:34, value:1000000000000000000000000000000000
            0x00000000, // precision:35, value:10000000000000000000000000000000000 
            0x00000000, // precision:36, value:100000000000000000000000000000000000 
            0x00000000, // precision:37, value:1000000000000000000000000000000000000
            0x00000000, // precision:38, value:10000000000000000000000000000000000000 
            0x00000000, // precision:38+1, value:99999999999999999999999999999999999999+1
        };

        static private readonly UInt32[] DecimalHelpersMid = { 
            0x00000000, // precision:2, value:10
            0x00000000, // precision:3, value:100 
            0x00000000, // precision:4, value:1000 
            0x00000000, // precision:5, value:10000
            0x00000000, // precision:6, value:100000 
            0x00000000, // precision:7, value:1000000
            0x00000000, // precision:8, value:10000000
            0x00000000, // precision:9, value:100000000
            0x00000000, // precision:10, value:1000000000 
            0x00000002, // precision:11, value:10000000000
            0x00000017, // precision:12, value:100000000000 
            0x000000e8, // precision:13, value:1000000000000 
            0x00000918, // precision:14, value:10000000000000
            0x00005af3, // precision:15, value:100000000000000 
            0x00038d7e, // precision:16, value:1000000000000000
            0x002386f2, // precision:17, value:10000000000000000
            0x01634578, // precision:18, value:100000000000000000
            0x0de0b6b3, // precision:19, value:1000000000000000000 
            0x8ac72304, // precision:20, value:10000000000000000000
            0x6bc75e2d, // precision:21, value:100000000000000000000 
            0x35c9adc5, // precision:22, value:1000000000000000000000 
            0x19e0c9ba, // precision:23, value:10000000000000000000000
            0x02c7e14a, // precision:24, value:100000000000000000000000 
            0x1bcecced, // precision:25, value:1000000000000000000000000
            0x16140148, // precision:26, value:10000000000000000000000000
            0xdcc80cd2, // precision:27, value:100000000000000000000000000
            0x9fd0803c, // precision:28, value:1000000000000000000000000000 
            0x3e250261, // precision:29, value:10000000000000000000000000000
            0x6d7217ca, // precision:30, value:100000000000000000000000000000 
            0x4674edea, // precision:31, value:1000000000000000000000000000000 
            0xc0914b26, // precision:32, value:10000000000000000000000000000000
            0x85acef81, // precision:33, value:100000000000000000000000000000000 
            0x38c15b0a, // precision:34, value:1000000000000000000000000000000000
            0x378d8e64, // precision:35, value:10000000000000000000000000000000000
            0x2b878fe8, // precision:36, value:100000000000000000000000000000000000
            0xb34b9f10, // precision:37, value:1000000000000000000000000000000000000 
            0x00f436a0, // precision:38, value:10000000000000000000000000000000000000
            0x098a2240, // precision:38+1, value:99999999999999999999999999999999999999+1 
        }; 

        static private readonly UInt32[] DecimalHelpersHi = { 
            0x00000000, // precision:2, value:10
            0x00000000, // precision:3, value:100
            0x00000000, // precision:4, value:1000
            0x00000000, // precision:5, value:10000 
            0x00000000, // precision:6, value:100000
            0x00000000, // precision:7, value:1000000 
            0x00000000, // precision:8, value:10000000 
            0x00000000, // precision:9, value:100000000
            0x00000000, // precision:10, value:1000000000 
            0x00000000, // precision:11, value:10000000000
            0x00000000, // precision:12, value:100000000000
            0x00000000, // precision:13, value:1000000000000
            0x00000000, // precision:14, value:10000000000000 
            0x00000000, // precision:15, value:100000000000000
            0x00000000, // precision:16, value:1000000000000000 
            0x00000000, // precision:17, value:10000000000000000 
            0x00000000, // precision:18, value:100000000000000000
            0x00000000, // precision:19, value:1000000000000000000 
            0x00000000, // precision:20, value:10000000000000000000
            0x00000005, // precision:21, value:100000000000000000000
            0x00000036, // precision:22, value:1000000000000000000000
            0x0000021e, // precision:23, value:10000000000000000000000 
            0x0000152d, // precision:24, value:100000000000000000000000
            0x0000d3c2, // precision:25, value:1000000000000000000000000 
            0x00084595, // precision:26, value:10000000000000000000000000 
            0x0052b7d2, // precision:27, value:100000000000000000000000000
            0x033b2e3c, // precision:28, value:1000000000000000000000000000 
            0x204fce5e, // precision:29, value:10000000000000000000000000000
            0x431e0fae, // precision:30, value:100000000000000000000000000000
            0x9f2c9cd0, // precision:31, value:1000000000000000000000000000000
            0x37be2022, // precision:32, value:10000000000000000000000000000000 
            0x2d6d415b, // precision:33, value:100000000000000000000000000000000
            0xc6448d93, // precision:34, value:1000000000000000000000000000000000 
            0xbead87c0, // precision:35, value:10000000000000000000000000000000000 
            0x72c74d82, // precision:36, value:100000000000000000000000000000000000
            0x7bc90715, // precision:37, value:1000000000000000000000000000000000000 
            0xd5da46d9, // precision:38, value:10000000000000000000000000000000000000
            0x5a86c47a, // precision:38+1, value:99999999999999999999999999999999999999+1
        };
 
        static private readonly UInt32[] DecimalHelpersHiHi = {
            0x00000000, // precision:2, value:10 
            0x00000000, // precision:3, value:100 
            0x00000000, // precision:4, value:1000
            0x00000000, // precision:5, value:10000 
            0x00000000, // precision:6, value:100000
            0x00000000, // precision:7, value:1000000
            0x00000000, // precision:8, value:10000000
            0x00000000, // precision:9, value:100000000 
            0x00000000, // precision:10, value:1000000000
            0x00000000, // precision:11, value:10000000000 
            0x00000000, // precision:12, value:100000000000 
            0x00000000, // precision:13, value:1000000000000
            0x00000000, // precision:14, value:10000000000000 
            0x00000000, // precision:15, value:100000000000000
            0x00000000, // precision:16, value:1000000000000000
            0x00000000, // precision:17, value:10000000000000000
            0x00000000, // precision:18, value:100000000000000000 
            0x00000000, // precision:19, value:1000000000000000000
            0x00000000, // precision:20, value:10000000000000000000 
            0x00000000, // precision:21, value:100000000000000000000 
            0x00000000, // precision:22, value:1000000000000000000000
            0x00000000, // precision:23, value:10000000000000000000000 
            0x00000000, // precision:24, value:100000000000000000000000
            0x00000000, // precision:25, value:1000000000000000000000000
            0x00000000, // precision:26, value:10000000000000000000000000
            0x00000000, // precision:27, value:100000000000000000000000000 
            0x00000000, // precision:28, value:1000000000000000000000000000
            0x00000000, // precision:29, value:10000000000000000000000000000 
            0x00000001, // precision:30, value:100000000000000000000000000000 
            0x0000000c, // precision:31, value:1000000000000000000000000000000
            0x0000007e, // precision:32, value:10000000000000000000000000000000 
            0x000004ee, // precision:33, value:100000000000000000000000000000000
            0x0000314d, // precision:34, value:1000000000000000000000000000000000
            0x0001ed09, // precision:35, value:10000000000000000000000000000000000
            0x00134261, // precision:36, value:100000000000000000000000000000000000 
            0x00c097ce, // precision:37, value:1000000000000000000000000000000000000
            0x0785ee10, // precision:38, value:10000000000000000000000000000000000000 
            0x4b3b4ca8, // precision:38+1, value:99999999999999999999999999999999999999+1 
        };
#endregion 

        // note that the algorithm covers a range from -5 to +4 from the initial index
        // at the end of the algorithm the tableindex will point to the greatest value that is
        // less than the current value 
        // except (!) if the current value is less than 10 (precision=1). There is no value ins
        // the table that is less than 10. In this case the algorithm terminates earlier. 
        // 
        // The startindex values have been chosen so that the highest possible index (startindex+5)
        // does not point to a value that has bits in a higher word set 
        //
        private const int HelperTableStartIndexLo = 5;
        private const int HelperTableStartIndexMid = 15;
        private const int HelperTableStartIndexHi = 24; 
        private const int HelperTableStartIndexHiHi = 33;
 
        private byte CalculatePrecision() { 
            int tableIndex;
            byte precision; 
            UInt32[] decimalHelpers;
            UInt32 decimalPart;

            if (m_data4 != 0) { 
                tableIndex = HelperTableStartIndexHiHi;
                decimalHelpers = DecimalHelpersHiHi; 
                decimalPart = m_data4; 
            }
            else if (m_data3 != 0) { 
                tableIndex = HelperTableStartIndexHi;
                decimalHelpers = DecimalHelpersHi;
                decimalPart = m_data3;
            } 
            else if (m_data2 != 0) {
                tableIndex = HelperTableStartIndexMid; 
                decimalHelpers = DecimalHelpersMid; 
                decimalPart = m_data2;
            } 
            else {
                tableIndex = HelperTableStartIndexLo;
                decimalHelpers = DecimalHelpersLo;
                decimalPart = m_data1; 
            }
 
 
            // this code will move the index no more than -2 -2 -1 (-5) or +2 +1 +1 (+4)
            // from the initial position 
            //
            if (decimalPart < decimalHelpers[tableIndex]) {
                tableIndex -= 2;
                if (decimalPart < decimalHelpers[tableIndex]) { 
                    tableIndex -= 2;
                    if (decimalPart < decimalHelpers[tableIndex]) { 
                        tableIndex -= 1; 
                    }
                    else { 
                        tableIndex += 1;
                    }
                }
                else { 
                    tableIndex += 1;
                } 
            } 
            else {
                tableIndex += 2; 
                if (decimalPart < decimalHelpers[tableIndex]) {
                    tableIndex -= 1;
                }
                else { 
                    tableIndex += 1;
                } 
            } 
            if (decimalPart >= decimalHelpers[tableIndex]) {
                tableIndex += 1; 
                if (tableIndex == 37 && decimalPart >= decimalHelpers[tableIndex]) {
                    // This can happen only if the actual value is greater than 1E+38,
                    // in which case, tableIndex starts at 33 and ends at 37.
                    // Note that in this case, the actual value will be out of SqlDeicmal's range, 
                    // and tableIndex is out of the array boudary. We'll throw later in ctors.
                    tableIndex += 1; 
                } 
            }
 
            precision = (byte)(tableIndex + 1);
            if (precision > 1) {
                // not done yet
                // tableIndex may still be off by one since we didn't look at the lower words 
                //
                if (VerifyPrecision((byte)(precision-1))) { 
                    precision -= 1; 
                }
            } 
            Debug.Assert ((precision == MaxPrecision+1) || VerifyPrecision(precision), "Calcualted precision too low?");
            Debug.Assert ((precision == 1) || !VerifyPrecision((byte)(precision-1)), "Calculated precision too high?");

            // adjust the precision 
            // This might not be correct but is to our best knowledge
            precision = Math.Max(precision, m_bScale); 
#if DEBUG 
            byte bPrec = m_bPrec;   // store current value
            m_bPrec = MaxPrecision; // BActualPrec does not work if m_bPrec uninitialized! 
            byte bActualPrecision = BActualPrec();
            m_bPrec=bPrec;  // restore current value

            Debug.Assert (precision==bActualPrecision,String.Format((IFormatProvider)null, "CalculatePrecision={0}, BActualPrec={1}. Results must be equal!", precision, bActualPrecision)); 
#endif
            return precision; 
        } 

        // VerifyPrecision 
        //
        // returns true if the current value is less or equal than the max value of the
        // supplied precision.
        // 
        private bool VerifyPrecision (byte precision) {
            Debug.Assert(precision>0,"Precision cannot be less than 1"); 
            Debug.Assert(precision<=MaxPrecision, "Precision > MaxPrecision"); 

            int  tableIndex = checked((int)precision -1); 
            if (m_data4 < DecimalHelpersHiHi[tableIndex]) {
                return true;
            }
            else if (m_data4 == DecimalHelpersHiHi[tableIndex]) { 
                if (m_data3 < DecimalHelpersHi[tableIndex]) {
                    return true; 
                } 
                else if (m_data3 == DecimalHelpersHi[tableIndex]) {
                    if (m_data2 < DecimalHelpersMid[tableIndex]) { 
                        return true;
                    }
                    else if (m_data2 == DecimalHelpersMid[tableIndex]) {
                        if (m_data1 < DecimalHelpersLo[tableIndex]) { 
                            return true;
                        } 
                    } 
                }
            } 
            return false;
        }

 

        // constructor 
        // construct a Null 
        private SqlDecimal(bool fNull) {
            m_bLen = 
            m_bPrec =
            m_bScale = (byte)0;
            m_bStatus = 0;
            m_data1 = 
            m_data2 =
            m_data3 = 
            m_data4 = x_uiZero; 
        }
 
        /// 
        ///    [To be supplied.]
        /// 
        public SqlDecimal(Decimal value) { 
            // set the null bit
            m_bStatus = x_bNotNull; 
 
            // note: using usafe code we could get the data directly out of the structure like that:
            // UInt32 * pInt = (UInt32*) &value; 
            // UInt32 sgnscl = *pInt++;

            // m_data3 = *pInt++; // high part
            // m_data1 = *pInt++; // lo part 
            // m_data2 = *pInt++; // mid part
 
            int[] bits = Decimal.GetBits(value); 

            UInt32 sgnscl = (UInt32)bits[3]; 
            m_data1 = (UInt32)bits[0];
            m_data2 = (UInt32)bits[1];
            m_data3 = (UInt32)bits[2];
            m_data4 = x_uiZero; 

            // set the sign bit 
            m_bStatus |= ((sgnscl & 0x80000000) == 0x80000000)? x_bNegative:(byte)0; 

            if (m_data3 != 0) 
                m_bLen = 3;
            else if (m_data2 != 0)
                m_bLen = 2;
            else 
                m_bLen = 1;
 
            // Get the scale info from Decimal 
            m_bScale = (byte)((int)(sgnscl & 0xff0000) >>16);
 
            // initialize precision
            m_bPrec = 0; // The this object cannot be used before all of its fields are assigned to

 
            m_bPrec = CalculatePrecision();
// CalculatePrecision adjusts! 
//            if (m_bPrec < m_bScale) 
//                m_bPrec = m_bScale;
        } 

        /// 
        ///    [To be supplied.]
        ///  
        public SqlDecimal(int value) {
            // set the null bit 
            m_bStatus = x_bNotNull; 

            uint uiValue = (uint) value; 

            // set the sign bit
            if (value < 0) {
                m_bStatus |= x_bNegative; 
                // The negative of -2147483648 doesn't fit into int32, directly cast to int should work.
                if (value != Int32.MinValue) 
                    uiValue = (uint)(- value); 
            }
 
            // set the data
            m_data1 = uiValue;
            m_data2 = m_data3 = m_data4 = x_uiZero;
 
            m_bLen = 1;
            m_bPrec = BGetPrecUI4(m_data1); 
            m_bScale = 0; 
        }
 
        /// 
        ///    [To be supplied.]
        /// 
        public SqlDecimal(long value) { 
            // set the null bit
            m_bStatus = x_bNotNull; 
 
            ulong dwl = (ulong)value;
 
            // set the sign bit
            if (value < 0) {
                m_bStatus |= x_bNegative;
                // The negative of Int64.MinValue doesn't fit into int64, directly cast to ulong should work. 
                if (value != Int64.MinValue)
                    dwl = (ulong)(- value); 
            } 

            // Copy DWL into bottom 2 UI4s of numeric 
            m_data1 = (uint)dwl;
            m_data2 = (uint)(dwl >> 32);
            m_data3 = m_data4 = 0;
 
            // Trim any leading zero from the length
            m_bLen = (byte)((m_data2 == 0) ? 1 : 2); 
 
            m_bPrec = BGetPrecUI8(dwl);
            m_bScale = 0; 

            AssertValid();
        }
 
        /// 
        ///    [To be supplied.] 
        ///  
        public SqlDecimal(byte bPrecision, byte bScale, bool fPositive, int[] bits) {
            CheckValidPrecScale(bPrecision, bScale); 
            if (bits == null)
                throw new ArgumentNullException("bits");
            else if (bits.Length != 4)
                throw new ArgumentException(SQLResource.InvalidArraySizeMessage, "bits"); 

            m_bPrec = bPrecision; 
            m_bScale = bScale; 
            m_data1 = (uint)bits[0];
            m_data2 = (uint)bits[1]; 
            m_data3 = (uint)bits[2];
            m_data4 = (uint)bits[3];
            m_bLen = 1;
            for (int i = 3; i >= 0; i --) { 
                if (bits[i] != 0) {
                    m_bLen = (byte)(i + 1); 
                    break; 
                }
            } 

            // set the null bit
            m_bStatus = x_bNotNull;
 
            // set the sign bit
            if (!fPositive) { 
                m_bStatus |= x_bNegative; 
            }
 
            // If result is -0, adjust sign to positive.
            if (FZero())
                SetPositive();
 
            if (bPrecision < CalculatePrecision())
                throw new OverflowException(SQLResource.ArithOverflowMessage); 
        } 

        ///  
        ///    [To be supplied.]
        /// 
        public SqlDecimal(byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4) {
            CheckValidPrecScale(bPrecision, bScale); 
            m_bPrec = bPrecision;
            m_bScale = bScale; 
 
            m_data1 = (uint)data1;
            m_data2 = (uint)data2; 
            m_data3 = (uint)data3;
            m_data4 = (uint)data4;

            m_bLen = 1; 
            if (data4 == 0)
                if (data3 == 0) 
                    if (data2 == 0) 
                        m_bLen = 1;
                    else 
                        m_bLen = 2;
                else
                    m_bLen = 3;
            else 
                m_bLen = 4;
 
            // set the null bit 
            m_bStatus = x_bNotNull;
 
            // set the sign bit
            if (!fPositive) {
                m_bStatus |= x_bNegative;
            } 

            // If result is -0, adjust sign to positive. 
            if (FZero()) 
                SetPositive();
 
            if (bPrecision < CalculatePrecision())
                throw new OverflowException(SQLResource.ArithOverflowMessage);
        }
 
        /// 
        ///    [To be supplied.] 
        ///  
        public SqlDecimal(double dVal) : this(false) {
            // set the null bit 
            m_bStatus = x_bNotNull;

            // Split double to sign, integer, and fractional parts
            if (dVal < 0) { 
                dVal = -dVal;
                m_bStatus |= x_bNegative; 
            } 

            // If it will not fit into numeric(NUMERIC_MAX_PRECISION,0), overflow. 
            if (dVal >= DMAX_NUME)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            double dInt = Math.Floor(dVal); 
            double dFrac = dVal - dInt;
 
            m_bPrec  = NUMERIC_MAX_PRECISION; 
            m_bLen = 1;
            if (dInt > 0.0) { 
                dVal = Math.Floor (dInt / DUINT_BASE);
                m_data1 = (uint)(dInt - dVal * DUINT_BASE);
                dInt = dVal;
 
                if (dInt > 0.0) {
                    dVal = Math.Floor (dInt / DUINT_BASE); 
                    m_data2 = (uint)(dInt - dVal * DUINT_BASE); 
                    dInt = dVal;
                    m_bLen ++; 

                    if (dInt > 0.0) {
                        dVal = Math.Floor (dInt / DUINT_BASE);
                        m_data3 = (uint)(dInt - dVal * DUINT_BASE); 
                        dInt = dVal;
                        m_bLen ++; 
 
                        if (dInt > 0.0) {
                            dVal = Math.Floor (dInt / DUINT_BASE); 
                            m_data4 = (uint) (dInt - dVal * DUINT_BASE);
                            dInt = dVal;
                            m_bLen ++;
                        } 
                    }
                } 
            } 

            uint ulLen, ulLenDelta; 
            uint ulTemp;

            // Get size of the integer part
            ulLen = FZero() ? 0 : (uint)CalculatePrecision(); 
            SQLDebug.Check(ulLen <= NUMERIC_MAX_PRECISION, "ulLen <= NUMERIC_MAX_PRECISION", "");
 
            // If we got more than 17 decimal digits, zero lower ones. 
            if (ulLen > DBL_DIG) {
                // Divide number by 10 while there are more then 17 digits 
                uint ulWrk = ulLen - DBL_DIG;
                do {
                    ulTemp = DivByULong (10);
                    ulWrk --; 
                }
                while (ulWrk > 0); 
                ulWrk = ulLen - DBL_DIG; 

                // Round, if necessary. # of digits can change. Cannot be overflow. 
                if (ulTemp >= 5) {
                    AddULong (1);
                    ulLen = CalculatePrecision() + ulWrk;
                } 

                // Multiply back 
                do { 
                    MultByULong (10);
                    ulWrk --; 
                }
                while (ulWrk > 0);
            }
 
            m_bScale = (byte)(ulLen < DBL_DIG ? DBL_DIG - ulLen : 0);
            m_bPrec = (byte)(ulLen + m_bScale); 
 
            // Add meaningful fractional part - max 9 digits per iteration
            if (m_bScale > 0) { 
                ulLen = m_bScale;
                do {
                    ulLenDelta = (ulLen >= 9) ? 9 : ulLen;
 
                    dFrac *= (double) x_rgulShiftBase[(int)ulLenDelta-1];
                    ulLen -= ulLenDelta; 
                    MultByULong (x_rgulShiftBase[(int)ulLenDelta-1]); 
                    AddULong ((uint) dFrac);
                    dFrac -= Math.Floor (dFrac); 
                }
                while (ulLen > 0);
            }
 
            // Round, if necessary
            if (dFrac >= 0.5) { 
                AddULong (1); 
            }
 
            if (FZero())
                SetPositive();

            AssertValid(); 
        }
 
        private SqlDecimal(uint[] rglData, byte bLen, byte bPrec, byte bScale, bool fPositive) { 
            CheckValidPrecScale(bPrec, bScale);
            SQLDebug.Check(rglData.Length >= 4); 

            m_bLen = bLen;
            m_bPrec = bPrec;
            m_bScale = bScale; 
            m_data1 = rglData[0];
            m_data2 = rglData[1]; 
            m_data3 = rglData[2]; 
            m_data4 = rglData[3];
 
            // set the null bit
            m_bStatus = x_bNotNull;

            // set the sign bit 
            if (!fPositive) {
                m_bStatus |= x_bNegative; 
            } 

            // If result is -0, adjust sign to positive. 
            if (FZero())
                SetPositive();
        }
 

        // INullable 
        ///  
        ///    [To be supplied.]
        ///  
        public bool IsNull {
            get { return(m_bStatus & x_bNullMask) == x_bIsNull;}
        }
 
        /// 
        ///    [To be supplied.] 
        ///  
        public Decimal Value {
            get { return ToDecimal();} 
        }

        /// 
        ///    [To be supplied.] 
        /// 
        public bool IsPositive { 
            get { 
                if (IsNull)
                    throw new SqlNullValueException(); 
                return(m_bStatus & x_bSignMask) == x_bPositive;
            }
        }
 
        private void SetPositive() {
            SQLDebug.Check(!IsNull); 
            m_bStatus = (byte)(m_bStatus & x_bReverseSignMask); 
        }
 
        private void SetSignBit(bool fPositive) {
            SQLDebug.Check(!IsNull);
            m_bStatus = (byte)(fPositive ? (m_bStatus & x_bReverseSignMask) : (m_bStatus | x_bNegative));
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        public byte Precision { 
            get {
                if (IsNull)
                    throw new SqlNullValueException();
                return m_bPrec; 
            }
        } 
 
        /// 
        ///    [To be supplied.] 
        /// 
        public byte Scale {
            get {
                if (IsNull) 
                    throw new SqlNullValueException();
                return m_bScale; 
            } 
        }
 
        /// 
        ///    [To be supplied.]
        /// 
        public int[] Data { 
            get {
                if (IsNull) 
                    throw new SqlNullValueException(); 
                return new int[4] { (int)m_data1, (int)m_data2, (int)m_data3, (int)m_data4};
            } 
        }

        /// 
        ///    [To be supplied.] 
        /// 
        public byte[] BinData { 
            get { 
                if (IsNull)
                    throw new SqlNullValueException(); 

                int data1 = (int)m_data1;
                int data2 = (int)m_data2;
                int data3 = (int)m_data3; 
                int data4 = (int)m_data4;
                byte[] rgBinData = new byte[16]; 
                rgBinData[0] = (byte)(data1 & 0xff); 
                data1 >>= 8;
                rgBinData[1] = (byte)(data1 & 0xff); 
                data1 >>= 8;
                rgBinData[2] = (byte)(data1 & 0xff);
                data1 >>= 8;
                rgBinData[3] = (byte)(data1 & 0xff); 
                rgBinData[4] = (byte)(data2 & 0xff);
                data2 >>= 8; 
                rgBinData[5] = (byte)(data2 & 0xff); 
                data2 >>= 8;
                rgBinData[6] = (byte)(data2 & 0xff); 
                data2 >>= 8;
                rgBinData[7] = (byte)(data2 & 0xff);
                rgBinData[8] = (byte)(data3 & 0xff);
                data3 >>= 8; 
                rgBinData[9] = (byte)(data3 & 0xff);
                data3 >>= 8; 
                rgBinData[10] = (byte)(data3 & 0xff); 
                data3 >>= 8;
                rgBinData[11] = (byte)(data3 & 0xff); 
                rgBinData[12] = (byte)(data4 & 0xff);
                data4 >>= 8;
                rgBinData[13] = (byte)(data4 & 0xff);
                data4 >>= 8; 
                rgBinData[14] = (byte)(data4 & 0xff);
                data4 >>= 8; 
                rgBinData[15] = (byte)(data4 & 0xff); 

                return rgBinData; 
            }
        }

        ///  
        ///    [To be supplied.]
        ///  
        public override String ToString() { 
            if (IsNull)
                return SQLResource.NullString; 
            AssertValid();

            // Make local copy of data to avoid modifying input.
            uint[]  rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4}; 
            int     culLen = m_bLen;
            char[]  pszTmp = new char[NUMERIC_MAX_PRECISION + 1];   //Local Character buffer to hold 
                                                                    //the decimal digits, from the 
                                                                    //lowest significant to highest significant
 
            int     iDigits = 0;//Number of significant digits
            uint    ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit

            // Build the final numeric string by inserting the sign, reversing 
            // the order and inserting the decimal number at the correct position
 
            //Retrieve each digit from the lowest significant digit 
            while (culLen > 1 || rgulNumeric[0] != 0) {
                MpDiv1 (rgulNumeric, ref culLen, x_ulBase10, out ulRem); 
                //modulo x_ulBase10 is the lowest significant digit
                pszTmp[iDigits++] = ChFromDigit(ulRem);
            }
 
            // if scale of the number has not been
            // reached pad remaining number with zeros. 
            while (iDigits <= m_bScale) { 
                pszTmp[iDigits++] = ChFromDigit(0);
            } 


            int uiResultLen = 0;
            int iCurChar = 0; 
            char[] szResult;
 
            // Increment the result length if scale > 0 (need to add '.') 
            if (m_bScale > 0) {
                uiResultLen = 1; 
            }

            if (IsPositive) {
                szResult = new char[uiResultLen + iDigits]; 
            }
            else { 
                // Increment the result length if negative (need to add '-') 
                szResult = new char[uiResultLen + iDigits + 1];
                szResult[iCurChar ++] = '-'; 
            }

            while (iDigits > 0) {
                if (iDigits-- == m_bScale) 
                    szResult[iCurChar ++] = '.';
 
                szResult[iCurChar ++] = pszTmp[iDigits]; 
            }
 
            AssertValid();

            return new String(szResult);
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal Parse(String s) { 
            if (s == null)
                throw new ArgumentNullException("s");

            if (s == SQLResource.NullString) 
                return SqlDecimal.Null;
 
            SqlDecimal snResult = SqlDecimal.Null; 

            char[] rgwchStr = s.ToCharArray(); 
            int cwchStr = rgwchStr.Length;

            int        iData;            //index to string
            char    usChar;            //current value in string 
            int        lDecPnt = -1;    //position of decimal point in string
            int     iCurChar = 0; 
 
            //Must initialize precision and scale to valid values
            snResult.m_bPrec  = 1; 
            snResult.m_bScale = 0;

            //Must initialize *this to zero
            snResult.SetToZero (); 

            // Trim trailing blanks. 
            while (cwchStr != 0 && rgwchStr[cwchStr-1] == ' ') 
                cwchStr--;
 
            // If string contains only spaces, stop
            if (cwchStr == 0)
                throw new FormatException(SQLResource.FormatMessage);
 
            // Trim leading blanks.
            while (rgwchStr[iCurChar] == ' ') { 
                iCurChar++; 
                cwchStr--;
            } 

            // Get sign for numeric value.
            if (rgwchStr[iCurChar] == '-') {
                snResult.SetSignBit(false); 
                iCurChar++;
                cwchStr--; 
            } 
            else {
                snResult.SetSignBit(true); 
                if (rgwchStr[iCurChar] == '+') {
                    iCurChar++;
                    cwchStr--;
                } 
            }
 
            // Hack: Check for "0.". If so, replace by ".0". 
            while ((cwchStr > 2) && (rgwchStr[iCurChar] == '0')) {
                iCurChar++; 
                cwchStr--;
            }
            if (2 == cwchStr && '0' == rgwchStr[iCurChar] && '.' == rgwchStr[iCurChar + 1]) {
                rgwchStr[iCurChar] = '.'; 
                rgwchStr[iCurChar + 1] = '0';
            } 
 
            // Invalid string?
            if (cwchStr == 0 || cwchStr > NUMERIC_MAX_PRECISION + 1) 
                throw new FormatException(SQLResource.FormatMessage);

            // Trim leading zeros.  (There shouldn't be any except for floats
            // less than 1.  e.g.  0.01) 
            while ((cwchStr > 1) && (rgwchStr[iCurChar] == '0')) {
                iCurChar++; 
                cwchStr--; 
            }
 
            // Convert string to numeric value by looping through input string.
            for(iData=0; iData < cwchStr; iData++)
            {
                usChar = rgwchStr[iCurChar]; 
                iCurChar ++;
 
                if (usChar >= '0' && usChar <= '9') 
                    usChar -= '0';
                else if (usChar == '.' && lDecPnt < 0) { 
                    lDecPnt = iData;
                    continue;
                }
                else 
                    throw new FormatException(SQLResource.FormatMessage);
 
                snResult.MultByULong(x_ulBase10); 
                snResult.AddULong(usChar);
            } 

            // Save precision and scale.
            if (lDecPnt < 0) {
                snResult.m_bPrec  = (byte)iData; 
                snResult.m_bScale = 0;
            } 
            else { 
                snResult.m_bPrec =  (byte)(iData - 1);
                snResult.m_bScale = (byte) (snResult.m_bPrec - lDecPnt); 
            }

            //Check for overflow condition
            if (snResult.m_bPrec > NUMERIC_MAX_PRECISION) 
                throw new FormatException(SQLResource.FormatMessage);
 
            // Check for invalid precision for numeric value. 
            // e.g., when string is ".", precision will be 0
            if (snResult.m_bPrec == 0) 
                throw new FormatException(SQLResource.FormatMessage);

            // If result is -0, adjust sign to positive.
            if (snResult.FZero()) 
                snResult.SetPositive();
 
            snResult.AssertValid(); 

            return snResult; 
        }

        /// 
        ///    [To be supplied.] 
        /// 
        public double ToDouble() { 
            if (IsNull) 
                throw new SqlNullValueException();
 
            double dRet = 0.0;

            dRet = (double)m_data4;
            dRet = dRet * x_lInt32Base + m_data3; 
            dRet = dRet * x_lInt32Base + m_data2;
            dRet = dRet * x_lInt32Base + m_data1; 
 
            dRet /= System.Math.Pow(10.0, m_bScale);
 
            return IsPositive ? dRet : - dRet;
        }

        private Decimal ToDecimal() { 
            if (IsNull)
                throw new SqlNullValueException(); 
 
            if ((int)m_data4 != 0 || m_bScale > 28)
                throw new OverflowException(SQLResource.ConversionOverflowMessage); 

            return new Decimal((int)m_data1, (int)m_data2, (int)m_data3, !IsPositive, m_bScale);
        }
 
        // Implicit conversion from Decimal to SqlDecimal
        ///  
        ///    [To be supplied.] 
        /// 
        public static implicit operator SqlDecimal(Decimal x) { 
            return new SqlDecimal(x);
        }

        // Explicit conversion from Double to SqlDecimal 
        public static explicit operator SqlDecimal(double x) {
            return new SqlDecimal(x); 
        } 

        // Implicit conversion from long to SqlDecimal 
        public static implicit operator SqlDecimal(long x) {
            return new SqlDecimal(new Decimal(x));
        }
 
        // Explicit conversion from SqlDecimal to Decimal. Throw exception if x is Null.
        ///  
        ///    [To be supplied.] 
        /// 
        public static explicit operator Decimal(SqlDecimal x) { 
            return x.Value;
        }

 
        // Unary operators
        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal operator -(SqlDecimal x) { 
            if (x.IsNull)
                return Null;
            else {
                SqlDecimal s = x; 
                if (s.FZero())
                    s.SetPositive(); 
                else 
                    s.SetSignBit(!s.IsPositive);
                return s; 
            }
        }

 
        // Binary operators
 
        // Arithmetic operators 
        /// 
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal operator +(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull)
                return Null; 

            ulong   dwlAccum;           //accumulated sum 
            bool    fMySignPos;         //sign of x was positive at start 
            bool    fOpSignPos;         // sign of y positive at start
            bool    fResSignPos = true; //sign of result should be positive 
            int     MyScale;    //scale of x
            int     OpScale;    //scale of y
            int     ResScale;   //scale of result
            int     ResPrec;    //precision of result 
            int     ResInteger; //number of digits for the integer part of result
            int     culOp1;     //# of UI4s in x 
            int     culOp2;     //# of UI4s in y 
            int     iulData;    //which UI4 we are operating on in x, y
            byte    bLen;       // length for the result 


            x.AssertValid();
            y.AssertValid(); 

            fMySignPos = x.IsPositive; 
            fOpSignPos = y.IsPositive; 

            //result scale = max(s1,s2) 
            //result precison = max(s1,s2) + max(p1-s1,p2-s2)
            MyScale = x.m_bScale;
            OpScale = y.m_bScale;
 
            // Calculate the integer part of the result.
            ResInteger = Math.Max((int)x.m_bPrec - MyScale, (int)y.m_bPrec - OpScale); 
            SQLDebug.Check (ResInteger <= MaxPrecision); 

            // Calculate the scale of the result. 
            ResScale = Math.Max(MyScale, OpScale);
            SQLDebug.Check (ResScale <= MaxScale);

            // Calculate the precision of the result. 
            // Add 1 for final carry.
            ResPrec = ResInteger + ResScale + 1; 
            ResPrec = Math.Min(MaxPrecision, ResPrec); 

            // If precision adjusted, scale is reduced to keep the integer part untruncated. 
            // But discard the extra carry, only keep the interger part as ResInteger, not ResInteger + 1.
            SQLDebug.Check(ResPrec - ResInteger >= 0);
            if (ResPrec - ResInteger < ResScale)
                ResScale = ResPrec - ResInteger; 

            // Adjust both operands to be the same scale as ResScale. 
            if (MyScale != ResScale) 
                x.AdjustScale(ResScale - MyScale, true);
 
            if (OpScale != ResScale)
                y.AdjustScale(ResScale - OpScale, true);

            // When sign of first operand is negative 
            // negate all operands including result.
            if (!fMySignPos) { 
                fMySignPos = !fMySignPos; 
                fOpSignPos = !fOpSignPos;
                fResSignPos = !fResSignPos; 
            }

            // Initialize operand lengths and pointer.
            culOp1 = x.m_bLen; 
            culOp2 = y.m_bLen;
 
            uint[] rglData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4}; 
            uint[] rglData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};
 
            if (fOpSignPos) {
                dwlAccum = 0;

                // 

                // Loop through UI4s adding operands and putting result in *this 
                // of the operands and put result in *this 
                for (iulData = 0; iulData < culOp1 || iulData < culOp2; iulData++) {
                    // None of these DWORDLONG additions can overflow, as dwlAccum comes in < x_lInt32Base 
                    if (iulData < culOp1)
                        dwlAccum += rglData1[iulData];
                    if (iulData < culOp2)
                        dwlAccum += rglData2[iulData]; 

                    rglData1[iulData] = (uint)dwlAccum; // equiv to mod x_lInt32Base 
                    dwlAccum >>= 32; // equiv to div x_lInt32Base 
                }
 
                //If carry
                if (dwlAccum != 0) {
                    SQLDebug.Check(dwlAccum < x_ulInt32Base);
 
                    //Either overflowed
                    if (iulData == x_cNumeMax) 
                        throw new OverflowException(SQLResource.ArithOverflowMessage); 

                    // Or extended length 
                    rglData1[iulData] = (uint)dwlAccum;
                    iulData ++;
                }
 
                // Set result length
                bLen = (byte) iulData; 
            } 
            else {
                int iulLastNonZero = 0; // The last nonzero UI 

                // When second operand is negative, switch operands
                // if operand2 is greater than operand1
                if (x.LAbsCmp (y) < 0) { 
                    fResSignPos = !fResSignPos;
                    uint[] rguiTemp = rglData2; 
                    rglData2 = rglData1; 
                    rglData1 = rguiTemp;
                    culOp1 = culOp2; 
                    culOp2 = x.m_bLen;
                }

                dwlAccum = x_ulInt32Base; 
                for (iulData = 0; iulData < culOp1 || iulData < culOp2; iulData++) {
                    if (iulData < culOp1) 
                        dwlAccum += rglData1[iulData]; 
                    if (iulData < culOp2)
                        dwlAccum -= rglData2[iulData]; 

                    rglData1[iulData] = (uint)dwlAccum; // equiv to mod BaseUI4
                    if (rglData1[iulData] != 0)
                        iulLastNonZero = iulData; 
                    dwlAccum >>= 32; // equiv to /= BaseUI4
                    dwlAccum += x_ulInt32BaseForMod; // equiv to BaseUI4 - 1 
                } 
                // Set length based on highest non-zero ULONG
                bLen = (byte) (iulLastNonZero + 1); 
            }

            SqlDecimal ret = new SqlDecimal(rglData1, bLen, (byte)ResPrec, (byte)ResScale, fResSignPos);
 
 			if (ret.FGt10_38() || ret.CalculatePrecision () > NUMERIC_MAX_PRECISION)
                throw new OverflowException(SQLResource.ArithOverflowMessage); 
 
            if (ret.FZero())
                ret.SetPositive(); 

            ret.AssertValid();

            return ret; 
        }
 
        ///  
        ///    [To be supplied.]
        ///  
        public static SqlDecimal operator -(SqlDecimal x, SqlDecimal y) {
            return x + (-y);
        }
 
        //    MultNm()
        // 
        //    Multiply two numerics. 
        //
        //  Parameters: 
        //        x    - IN Multiplier
        //        y    - IN Multiplicand
        //
        //    Result scale and precision(same as in SQL Server Manual and Hydra): 
        //        scale = s1 + s2
        //        precison = s1 + s2 + (p1 - s1) + (p2 - s2) + 1 
        // 
        //    Overflow Rules:
        //        If scale is greater than NUMERIC_MAX_PRECISION it is set to 
        //    NUMERIC_MAX_PRECISION.  If precision is greater than NUMERIC_MAX_PRECISION
        //    it is set to NUMERIC_MAX_PRECISION, then scale is reduced to keep the
        //    integer part untruncated but keeping a minimum value of x_cNumeDivScaleMin.
        //    For example, if using the above formula, the resulting precision is 46 and 
        //    scale is 10, the precision will be reduced to 38. To keep the integral part
        //    untruncated the scale needs be reduced to 2, but since x_cNumeDivScaleMin 
        //    is set to 6 currently, resulting scale will be 6. 
        //        O_OVERFLOW is returned only if the actual precision is greater than
        //     NUMERIC_MAX_PRECISION or the actual length is greater than x_cbNumeBuf. 
        //
        //    Algorithm:
        //        Starting from the lowest significant UI4, for each UI4 of the multiplier
        //    iterate through the UI4s of the multiplicand starting from 
        //    the least significant UI4s, multiply the multiplier UI4 with
        //    multiplicand UI4, update the result buffer with the product modulo 
        //    x_dwlBaseUI4 at the same index as the multiplicand, and carry the quotient to 
        //    add to the next multiplicand UI4.  Until the end of the multiplier data
        //    array is reached. 
        //
        /// 
        ///    [To be supplied.]
        ///  
        public static SqlDecimal operator *(SqlDecimal x, SqlDecimal y) {
            x.AssertValid(); 
            y.AssertValid(); 

            if (x.IsNull || y.IsNull) 
                return Null;

            //Implementation:
            //        I) Figure result scale,prec 
            //        II) Perform mult.
            //        III) Adjust product to result scale,prec 
 
            // Local variables for actual multiplication
            int     iulPlier;           //index of UI4 in the Multiplier 
            uint    ulPlier;            //current mutiplier UI4
            ulong   dwlAccum;           //accumulated sum
            ulong   dwlNextAccum;       //overflow of accumulated sum
            int     culCand = y.m_bLen; //length of multiplicand in UI4s 

            //Local variables to track scale,precision 
            int ActualScale;                    // Scale after mult done 
            int ResScale;                       // Final scale we will force result to
            int ResPrec;                        // Final precision we will force result to 
            int ResInteger;                     // # of digits in integer part of result (prec-scale)
            int lScaleAdjust;   //How much result scale will be adjusted
            bool fResPositive;  // Result sign
 
            SqlDecimal  ret;
 
 
            //I) Figure result prec,scale
            ActualScale = x.m_bScale + y.m_bScale; 
            ResScale = ActualScale;
            ResInteger = (x.m_bPrec - x.m_bScale) + (y.m_bPrec - y.m_bScale) + 1;

            //result precison = s1 + s2 + (p1 - s1) + (p2 - s2) + 1 
            ResPrec = ResScale + ResInteger;
 
            // Downward adjust res prec,scale if either larger than NUMERIC_MAX_PRECISION 
            if (ResPrec >  NUMERIC_MAX_PRECISION)
                ResPrec = NUMERIC_MAX_PRECISION; 
            if (ResScale > NUMERIC_MAX_PRECISION)
                ResScale = NUMERIC_MAX_PRECISION;

            // 
            // It is possible when two large numbers are being multiplied the scale
            // can be reduced to 0 to keep data untruncated; the fix here is to 
            // preserve a minimum scale of 6. 
            //
            // If overflow, reduce the scale to avoid truncation of data 
            ResScale = Math.Min((ResPrec - ResInteger),ResScale);
            // But keep a minimum scale of NUMERIC_MIN_DVSCALE
            ResScale = Math.Max(ResScale, Math.Min(ActualScale,x_cNumeDivScaleMin));
 
            lScaleAdjust = ResScale - ActualScale;
 
            fResPositive = (x.IsPositive == y.IsPositive);//positive if both signs same. 

            // II) Perform multiplication 

            uint[] rglData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4};
            uint[] rglData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};
 
            //Local buffer to hold the result of multiplication.
            //Longer than CReNumeBuf because full precision of multiplication is carried out 
            const int   x_culNumeMultRes = 9;       // Maximum # UI4s in result buffer in multiplication 
            uint[]      rgulRes = new uint[x_culNumeMultRes]; //new [] are already initialized to zero
            int         culRes;             // # of UI4s in result 
            int         idRes = 0;

            //Iterate over the bytes of multiplier
            for (iulPlier = 0; iulPlier < x.m_bLen; iulPlier++) { 
                ulPlier = rglData1[iulPlier];
                dwlAccum = 0; 
 
                //Multiply each UI4 of multiCand by ulPliear and accumulate into result buffer
 
                // Start on correct place in result
                idRes = iulPlier;

                for (int iulCand = 0; iulCand < culCand; iulCand++) { 
                    // dwlAccum = dwlAccum + rgulRes[idRes] + ulPlier*rglData2[iulCand]
                    //        use dwlNextAccum to detect overflow of DWORDLONG 
                    dwlNextAccum = dwlAccum + rgulRes[idRes]; 
                    ulong ulTemp = (ulong)rglData2[iulCand];
                    dwlAccum =  (ulong)ulPlier * ulTemp; 
                    dwlAccum += dwlNextAccum;
                    if (dwlAccum < dwlNextAccum) // indicates dwl addition overflowed
                        dwlNextAccum = x_ulInt32Base; // = maxUI64/x_dwlBaseUI4
                    else 
                        dwlNextAccum = 0;
 
                    // Update result and accum 
                    rgulRes[idRes ++] = (uint)(dwlAccum);// & x_ulInt32BaseForMod); // equiv to mod x_lInt32Base
                    dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equiv to div BaseUI4 + dwlNAccum 

                    // dwlNextAccum can't overflow next iteration
                    SQLDebug.Check(dwlAccum < x_ulInt32Base*2);
                } 

                SQLDebug.Check(dwlAccum < x_ulInt32Base); // can never final accum > 1 more UI4 
                if (dwlAccum != 0) 
                    rgulRes[idRes ++] = (uint)dwlAccum;
            } 
            // Skip leading 0s (may exist if we are multiplying by 0)
            for (;(rgulRes[idRes] == 0) && (idRes > 0); idRes--)
                ;
            // Calculate actual result length 
            culRes = idRes + 1;
 
            // III) Adjust precision,scale to result prec,scale 
            if (lScaleAdjust != 0) {
                // If need to decrease scale 
                if (lScaleAdjust < 0) {
                    SQLDebug.Check(NUMERIC_MAX_PRECISION == ResPrec);

                    // have to adjust - might yet end up fitting. 
                    // Cannot call AdjustScale - number cannot fit in a numeric, so
                    // have to duplicate code here 
 
                    uint    ulRem;          //Remainder when downshifting
                    uint    ulShiftBase;    //What to multiply by to effect scale adjust 

                    do {
                        if (lScaleAdjust <= -9) {
                            ulShiftBase = x_rgulShiftBase[8]; 
                            lScaleAdjust += 9;
                        } 
                        else { 
                            ulShiftBase = x_rgulShiftBase[-lScaleAdjust - 1];
                            lScaleAdjust = 0; 
                        }
                        MpDiv1 (rgulRes, ref culRes, ulShiftBase, out ulRem);
                    }
                    while (lScaleAdjust != 0); 

                    // Still do not fit? 
                    if (culRes > x_cNumeMax) 
                        throw new OverflowException(SQLResource.ArithOverflowMessage);
 
                    for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                        rgulRes[idRes] = 0;
                    ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ResScale, fResPositive);
 
                    // Is it greater than 10**38?
                    if (ret.FGt10_38()) 
                        throw new OverflowException(SQLResource.ArithOverflowMessage); 

                    ret.AssertValid(); 

                    // If remainder is 5 or above, increment/decrement by 1.
                    if (ulRem >= ulShiftBase/2)
                        ret.AddULong(1); 
                    // After adjusting, if the result is 0 and remainder is less than 5,
                    // set the sign to be positive 
                    if (ret.FZero ()) 
                        ret.SetPositive();
 
                    return ret;
                }

                // Otherwise call AdjustScale 
                if (culRes > x_cNumeMax)    // Do not fit now, so will not fit after asjustement
                    throw new OverflowException(SQLResource.ArithOverflowMessage); 
                // NOTE: Have not check for value in the range (10**38..2**128), 
                // as we'll call AdjustScale with positive argument, and it'll
                // return "normal" overflow 

                for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                    rgulRes[idRes] = 0;
                ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ActualScale, fResPositive); 

                if (ret.FZero ()) 
                    ret.SetPositive(); 

                ret.AssertValid(); 

                ret.AdjustScale(lScaleAdjust, true);

                return ret; 
            }
            else { 
                if (culRes > x_cNumeMax) 
                    throw new OverflowException(SQLResource.ArithOverflowMessage);
 
                for (idRes = culRes; idRes < x_cNumeMax; idRes ++)
                    rgulRes[idRes] = 0;
                ret = new SqlDecimal(rgulRes, (byte)culRes, (byte)ResPrec, (byte)ResScale, fResPositive);
 
                // Is it greater than 10**38?
                if (ret.FGt10_38 ()) 
                    throw new OverflowException(SQLResource.ArithOverflowMessage); 

                if (ret.FZero ()) 
                    ret.SetPositive();

                ret.AssertValid();
 
                return ret;
            } 
        } 

        //------------------------------------------------------------ 
        //DivNm():
        //  Divide numeric by numeric.
        //    The Quotient will be returned in *this
        // 
        //Result scale&precision:
        //    NOTE: same as in Hydra but different from SQL Server Manual, 
        //            where scale = max(s1+p2-s2+1,x_cNumeDivScaleMin)): 
        //        scale = max(s1 + p2 + 1, x_cNumeDivScaleMin);
        //        precision = max(s1 + p2 + 1, x_cNumeDivScaleMin) + p1 + p2 + 1; 
        //
        //Overflow Rules:
        //        If scale is greater than NUMERIC_MAX_PRECISION it is set to
        //    NUMERIC_MAX_PRECISION.  If precision is greater than NUMERIC_MAX_PRECISION 
        //    it's set to NUMERIC_MAX_PRECISION, then scale is reduced to keep the
        //    integer part untruncated but keeping a minimum value of x_cNumeDivScaleMin. 
        //    For example, if using the above formula, the resulting precision is 46 and 
        //    scale is 10, the precision will be reduced to 38, to keep the integral part
        //    untruncated the scale needs be recuded to 2, but since x_cNumeDivScaleMin 
        //    is set to 6 currently, resulting scale will be 6.
        //        OverflowException is throwed only if the actual precision is greater than
        //    NUMERIC_MAX_PRECISION or actual length is greater than x_cbNumeBuf
        // 
        //Algorithm
        //  Call general purpose arbitrary precision division routine with scale = 0. 
        //    Scale,prec adjusted later. 
        //
        ///  
        ///    [To be supplied.]
        /// 
        public static SqlDecimal operator /(SqlDecimal x, SqlDecimal y) {
            if (x.IsNull || y.IsNull) 
                return Null;
 
            x.AssertValid(); 
            y.AssertValid();
 
            // Variables for figuring prec,scale
            int bScaleD;            // Input Scale of dividend (output scale of remainder)
            int bPrecD;             // Input Prec of dividend (output prec of remainder)
            int ResScale;           // Final scale we will force quotient to 
            int ResPrec;            // Final precision we will force quotient to
            int ResInteger;         // # of digits in integer part of result (prec-scale) 
            int MinScale;           // Temp to help compute ResScale 
            int lScaleAdjust;       // How much result scale will be adjusted
            bool fResSignPos;       // sign of result 

            // Steps:
            //    1) Figure result prec,scale; adjust scale of dividend
            //    2) Compute result remainder/quotient in 0 scale numbers 
            //    3) Set result prec,scale and adjust as necessary
 
            // 0) Check for Div by 0 
            if (y.FZero())
                throw new DivideByZeroException(SQLResource.DivideByZeroMessage); 

            // 1) Figure out result prec,scale,sign..
            fResSignPos = (x.IsPositive == y.IsPositive);//sign of result
 
            //scale = max(s1 + p2 + 1, x_cNumeDivScaleMin);
            //precision = max(s1 + p2 + 1, x_cNumeDivScaleMin) + p1 + p2 + 1; 
            //For backward compatibility, use exactly the same scheme as in Hydra 
            bScaleD = x.m_bScale;
            bPrecD = x.m_bPrec; 
            ResScale = Math.Max(x.m_bScale + y.m_bPrec + 1, x_cNumeDivScaleMin);
            ResInteger = x.m_bPrec - x.m_bScale + y.m_bScale;
            ResPrec = ResScale + x.m_bPrec + y.m_bPrec + 1;
            MinScale = Math.Min(ResScale, x_cNumeDivScaleMin); 

            ResInteger = Math.Min(ResInteger, NUMERIC_MAX_PRECISION); 
            ResPrec = ResInteger + ResScale; 

            if (ResPrec >  NUMERIC_MAX_PRECISION) 
                ResPrec = NUMERIC_MAX_PRECISION;

            // If overflow, reduce the scale to avoid truncation of data
            ResScale = Math.Min((ResPrec - ResInteger), ResScale); 
            ResScale = Math.Max(ResScale, MinScale);
 
            //Adjust the scale of the dividend 
            lScaleAdjust = ResScale - (int)x.m_bScale + (int)y.m_bScale;
            x.AdjustScale(lScaleAdjust, true); 

            // Step2: Actual Computation

            uint[] rgulData1 = new uint[4] {x.m_data1, x.m_data2, x.m_data3, x.m_data4}; 
            uint[] rgulData2 = new uint[4] {y.m_data1, y.m_data2, y.m_data3, y.m_data4};
 
            // Buffers for arbitrary precision divide 
            uint[] rgulR = new uint[x_cNumeMax + 1];
            uint[] rgulQ = new uint[x_cNumeMax]; 
            // # of ULONGs in result
            int culQ, culR;

            // Divide mantissas. V is not zero - already checked. 
            // Cannot overflow, as Q <= U, R <= V. (and both are positive)
            MpDiv(rgulData1, x.m_bLen, rgulData2, y.m_bLen, rgulQ, out culQ, rgulR, out culR); 
 
            // Construct the result from Q
            ZeroToMaxLen (rgulQ, culQ); 
            SqlDecimal ret = new SqlDecimal(rgulQ, (byte)culQ, (byte)ResPrec, (byte)ResScale, fResSignPos);

            if (ret.FZero())
                ret.SetPositive(); 

            ret.AssertValid(); 
 
            return ret;
        } 



        // Implicit conversions 

        // Implicit conversion from SqlBoolean to SqlDecimal 
        ///  
        ///    [To be supplied.]
        ///  
        public static explicit operator SqlDecimal(SqlBoolean x) {
            return x.IsNull ? Null : new SqlDecimal((int)x.ByteValue);
        }
 
        // Implicit conversion from SqlByte to SqlDecimal
        ///  
        ///    [To be supplied.] 
        /// 
        public static implicit operator SqlDecimal(SqlByte x) { 
            return x.IsNull ? Null : new SqlDecimal((int)(x.Value));
        }

        // Implicit conversion from SqlInt16 to SqlDecimal 
        /// 
        ///    [To be supplied.] 
        ///  
        public static implicit operator SqlDecimal(SqlInt16 x) {
            return x.IsNull ? Null : new SqlDecimal((int)(x.Value)); 
        }

        // Implicit conversion from SqlInt32 to SqlDecimal
        ///  
        ///    [To be supplied.]
        ///  
        public static implicit operator SqlDecimal(SqlInt32 x) { 
            return x.IsNull ? Null : new SqlDecimal(x.Value);
        } 

        // Implicit conversion from SqlInt64 to SqlDecimal
        /// 
        ///    [To be supplied.] 
        /// 
        public static implicit operator SqlDecimal(SqlInt64 x) { 
            return x.IsNull ? Null : new SqlDecimal(x.Value); 
        }
 
        // Implicit conversion from SqlMoney to SqlDecimal
        /// 
        ///    [To be supplied.]
        ///  
        public static implicit operator SqlDecimal(SqlMoney x) {
            return x.IsNull ? Null : new SqlDecimal(x.ToDecimal()); 
        } 

 
        // Explicit conversions

         // Explicit conversion from SqlSingle to SqlDecimal
        ///  
        ///    [To be supplied.]
        ///  
        public static explicit operator SqlDecimal(SqlSingle x) { 
            return x.IsNull ? SqlDecimal.Null : new SqlDecimal((double)(x.Value));
        } 

       // Explicit conversion from SqlDouble to SqlDecimal
        /// 
        ///    [To be supplied.] 
        /// 
        public static explicit operator SqlDecimal(SqlDouble x) { 
            return x.IsNull ? SqlDecimal.Null : new SqlDecimal(x.Value); 
        }
 
        // Explicit conversion from SqlString to SqlDecimal
        // Throws FormatException or OverflowException if necessary.
        /// 
        ///    [To be supplied.] 
        /// 
        public static explicit operator SqlDecimal(SqlString x) { 
            return x.IsNull ? Null : SqlDecimal.Parse(x.Value); 
        }
 
        // private methods

        //---------------------------------------------------------------------
        // Is this RE numeric valid? 
        [System.Diagnostics.Conditional("DEBUG")]
        private void AssertValid() { 
            if (IsNull) 
                return;
 
            // Scale,Prec in range
            SQLDebug.Check(m_bScale <= NUMERIC_MAX_PRECISION, "m_bScale <= NUMERIC_MAX_PRECISION", "In AssertValid");
            SQLDebug.Check(m_bScale <= m_bPrec, "m_bScale <= m_bPrec", "In AssertValid");
            SQLDebug.Check(m_bScale >= 0, "m_bScale >= 0", "In AssertValid"); 
            SQLDebug.Check(m_bPrec > 0, "m_bPrec > 0", "In AssertValid");
 
            SQLDebug.Check(CLenFromPrec(m_bPrec) >= m_bLen, "CLenFromPrec(m_bPrec) >= m_bLen", "In AssertValid"); 
            SQLDebug.Check(m_bLen <= x_cNumeMax, "m_bLen <= x_cNumeMax", "In AssertValid");
 
            uint[] rglData = new uint[4] {m_data1, m_data2, m_data3, m_data4};

            // highest UI4 is non-0 unless value "zero"
            if (rglData[m_bLen-1] == 0) { 
                SQLDebug.Check(m_bLen == 1, "m_bLen == 1", "In AssertValid");
            } 
 
            // All UI4s from length to end are 0
            for (int iulData = m_bLen; iulData < x_cNumeMax; iulData++) 
                SQLDebug.Check(rglData[iulData] == 0, "rglData[iulData] == 0", "In AssertValid");
        }
/*
        // print the data members 
        [System.Diagnostics.Conditional("DEBUG")]
        private void Print() { 
            if (IsNull) { 
                Debug.WriteLine("Numeric: Null");
                return; 
            }
            Debug.WriteLine("Numeric: data - " + m_data4.ToString() + ", " + m_data3.ToString() + ", " +
                              m_data2.ToString() + ", " + m_data1.ToString());
            Debug.WriteLine("\tlen = " + m_bLen.ToString() + ", Prec = " + m_bPrec.ToString() + 
                              ", Scale = " + m_bScale.ToString() + ", Sign = " + IsPositive.ToString());
        } 
 
        [System.Diagnostics.Conditional("DEBUG")]
        private void Print(String s) { 
            Debug.WriteLine("*** " + s + " ***");
            if (IsNull) {
                Debug.WriteLine("Numeric: Null");
                return; 
            }
            Debug.WriteLine("Numeric: data - " + m_data4.ToString() + ", " + m_data3.ToString() + ", " + 
                              m_data2.ToString() + ", " + m_data1.ToString()); 
            Debug.WriteLine("\tlen = " + m_bLen.ToString() + ", Prec = " + m_bPrec.ToString() +
                              ", Scale = " + m_bScale.ToString() + ", Sign = " + IsPositive.ToString()); 
        }
*/
        // Set all extra uints to zero
        private static void ZeroToMaxLen(uint[] rgulData, int cUI4sCur) { 
            SQLDebug.Check(rgulData.Length == x_cNumeMax, "rgulData.Length == x_cNumeMax", "Invalid array length");
 
            switch (cUI4sCur) { 
                case 1:
                    rgulData[1] = rgulData[2] = rgulData[3] = 0; 
                    break;

                case 2:
                    rgulData[2] = rgulData[3] = 0; 
                    break;
 
                case 3: 
                    rgulData[3] = 0;
                    break; 
            }
        }

        // Set all extra uints to zero 
        /*
        private void ZeroToMaxLen(int cUI4sCur) { 
            switch (cUI4sCur) { 
                case 1:
                    m_data2 = m_data3 = m_data4 = x_uiZero; 
                    break;

                case 2:
                    m_data3 = m_data4 = x_uiZero; 
                    break;
 
                case 3: 
                    m_data4 = x_uiZero;
                    break; 
            }
        }
        */
 
        //Determine the number of uints needed for a numeric given a precision
        //Precision        Length 
        //    0            invalid 
        //    1-9            1
        //    10-19        2 
        //    20-28        3
        //    29-38        4
        // The array in Shiloh. Listed here for comparison.
        //private static readonly byte[] rgCLenFromPrec = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9, 
        //    9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
        private static readonly byte[] rgCLenFromPrec = new byte[] {1,1,1,1,1,1,1,1,1,2,2,2,2,2, 
            2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4}; 

        private static byte CLenFromPrec(byte bPrec) { 
            SQLDebug.Check(bPrec <= MaxPrecision && bPrec > 0, "bPrec <= MaxPrecision && bPrec > 0",
                           "Invalid numeric precision");
            return rgCLenFromPrec[bPrec-1];
        } 

        // check whether is zero 
        private bool FZero() { 
            return(m_data1 == 0) && (m_bLen <= 1);
        } 

        // Find the case where we overflowed 10**38, but not 2**128
        private bool FGt10_38() {
            return    m_data4 >= 0x4b3b4ca8L && m_bLen == 4 && 
            ((m_data4 > 0x4b3b4ca8L) || (m_data3 > 0x5a86c47aL) ||
             (m_data3 == 0x5a86c47aL) && (m_data2 >= 0x098a2240L)); 
        } 

        private bool FGt10_38(uint[] rglData) { 
            SQLDebug.Check(rglData.Length == 4, "rglData.Length == 4", "Wrong array length: " + rglData.Length.ToString(CultureInfo.InvariantCulture));

            return    rglData[3] >= 0x4b3b4ca8L &&
            ((rglData[3] > 0x4b3b4ca8L) || (rglData[2] > 0x5a86c47aL) || 
             (rglData[2] == 0x5a86c47aL) && (rglData[1] >= 0x098a2240L));
        } 
 

        // Powers of ten (used by BGetPrecI4,BGetPrecI8) 
        private static readonly uint x_ulT1 =       10;
        private static readonly uint x_ulT2 =       100;
        private static readonly uint x_ulT3 =       1000;
        private static readonly uint x_ulT4 =       10000; 
        private static readonly uint x_ulT5 =       100000;
        private static readonly uint x_ulT6 =       1000000; 
        private static readonly uint x_ulT7 =       10000000; 
        private static readonly uint x_ulT8 =       100000000;
        private static readonly uint x_ulT9 =       1000000000; 
        private static readonly ulong x_dwlT10 =    10000000000;
        private static readonly ulong x_dwlT11 =    100000000000;
        private static readonly ulong x_dwlT12 =    1000000000000;
        private static readonly ulong x_dwlT13 =    10000000000000; 
        private static readonly ulong x_dwlT14 =    100000000000000;
        private static readonly ulong x_dwlT15 =    1000000000000000; 
        private static readonly ulong x_dwlT16 =    10000000000000000; 
        private static readonly ulong x_dwlT17 =    100000000000000000;
        private static readonly ulong x_dwlT18 =    1000000000000000000; 
        private static readonly ulong x_dwlT19 =    10000000000000000000;

        //------------------------------------------------------------------
        //BGetPrecI4 
        //    Return the precision(number of significant digits) of a integer
        private static byte BGetPrecUI4(uint value) { 
            int ret; 

            // Now do the (almost) binary search 
            if (value < x_ulT4) {
                if (value < x_ulT2)
                    ret = value >= x_ulT1 ? 2 : 1;
                else 
                    ret = value >= x_ulT3 ? 4 : 3;
            } 
            else if (value < x_ulT8) { 
                if (value < x_ulT6)
                    ret = value >= x_ulT5 ? 6 : 5; 
                else
                    ret = value >= x_ulT7 ? 8 : 7;
            }
            else 
                ret = value >= x_ulT9 ? 10 : 9;
 
            return(byte)ret; 
        }
 
        //------------------------------------------------------------------
        //BGetPrecI8
        //    Return the precision (number of significant digits) of an Int8
        private static byte  BGetPrecUI8(uint  ulU0, uint ulU1) { 
            ulong dwlVal = ((ulong) ulU0) + (((ulong) ulU1) << 32);
            return BGetPrecUI8 (dwlVal); 
        } 

        private static byte  BGetPrecUI8(ulong dwlVal) { 
            int ret;

            // Now do the (almost) binary search
            if (dwlVal < x_ulT8) { 
                uint ulVal = (uint) dwlVal;
 
                if (ulVal < x_ulT4) { 
                    if (ulVal < x_ulT2)
                        ret = (ulVal >= x_ulT1) ? 2 : 1; 
                    else
                        ret = (ulVal >= x_ulT3) ? 4 : 3;
                }
                else { 
                    if (ulVal < x_ulT6)
                        ret = (ulVal >= x_ulT5) ? 6 : 5; 
                    else 
                        ret = (ulVal >= x_ulT7) ? 8 : 7;
                } 
            }
            else if (dwlVal < x_dwlT16) {
                if (dwlVal < x_dwlT12) {
                    if (dwlVal < x_dwlT10) 
                        ret = (dwlVal >= x_ulT9) ? 10 : 9;
                    else 
                        ret = (dwlVal >= x_dwlT11) ? 12 : 11; 
                }
                else { 
                    if (dwlVal < x_dwlT14)
                        ret = (dwlVal >= x_dwlT13) ? 14 : 13;
                    else
                        ret = (dwlVal >= x_dwlT15) ? 16 : 15; 
                }
            } 
            else { 
                if (dwlVal < x_dwlT18)
                    ret = (dwlVal >= x_dwlT17) ? 18 : 17; 
                else
                    ret = (dwlVal >= x_dwlT19) ? 20 : 19;
            }
 
            return(byte)ret;
        } 
 
#if DEBUG
        // This is the old precision routine. It is only used in debug code to verify that both code paths 
        // return the same value

        //
        //    BActualPrec() 
        //
        //    Determine the actual number of significant digits (precision) of a numeric 
        // 
        //    Parameters:
        // 
        //    Complexity:
        //        For small numerics, use simpler routines = O(n)
        //        Else, max 3 divisions of mp by ULONG, then again simpler routine.
        // 
        //    Returns:
        //        a byte containing the actual precision 
        // 
        private byte BActualPrec () {
            if (m_bPrec == 0 || m_bLen < 1) 
                return 0;

            int     ciulU = m_bLen;
            int     Prec; 
            uint    ulRem;
 
            if (ciulU == 1) { 
                Prec = BGetPrecUI4 (m_data1);
            } 
            else if (ciulU == 2) {
                Prec = BGetPrecUI8 (m_data1, m_data2);
            }
            else { 
                uint[]  rgulU = new uint[4] { m_data1, m_data2, m_data3, m_data4};
                Prec = 0; 
                do { 
                    MpDiv1 (rgulU, ref ciulU, 1000000000, out ulRem);
                    Prec += 9; 
                }
                while (ciulU > 2);
                SQLDebug.Check (Prec == 9 || Prec == 18 || Prec == 27);
                Prec += BGetPrecUI8 (rgulU[0], rgulU[1]); 
            }
 
            // If number of significant digits less than scale, return scale 
            return(Prec < m_bScale ? m_bScale : (byte)Prec);
        } 
#endif

        //    AddULong()
        // 
        //    Add ulAdd to this numeric.  The result will be returned in *this.
        // 
        //    Parameters: 
        //        this    - IN Operand1 & OUT Result
        //        ulAdd    - IN operand2. 
        //
        private void AddULong(uint ulAdd) {
            ulong   dwlAccum = (ulong) ulAdd;
            int     iData;                  // which UI4 in this we are on 
            int     iDataMax = (int)m_bLen; // # of UI4s in this
 
            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4}; 

            // Add, starting at the LS UI4 until out of UI4s or no carry 
            iData = 0;
            do {
                dwlAccum += (ulong) rguiData[iData];
                rguiData[iData] = (uint)dwlAccum;       // equivalent to mod x_dwlBaseUI4 
                dwlAccum >>= 32;                        // equivalent to dwlAccum /= x_dwlBaseUI4;
                if (0 == dwlAccum) { 
                    StoreFromWorkingArray(rguiData); 
                    return;
                } 
                iData ++;
            }
            while (iData < iDataMax);
 
            // There is carry at the end
 
            SQLDebug.Check(dwlAccum < x_ulInt32Base, "dwlAccum < x_lInt32Base", ""); 

            // Either overflowed 
            if (iData == x_cNumeMax)
                throw new OverflowException(SQLResource.ArithOverflowMessage);

            // Or need to extend length by 1 UI4 
            rguiData[iData] = (uint)dwlAccum;
            m_bLen ++; 
 
            if (FGt10_38 (rguiData))
                throw new OverflowException(SQLResource.ArithOverflowMessage); 

            StoreFromWorkingArray(rguiData);
        }
 
        // multiply by a long integer
        private void MultByULong(uint uiMultiplier) { 
            int     iDataMax = m_bLen; // How many UI4s currently in *this 

            ulong   dwlAccum = 0;       // accumulated sum 
            ulong   dwlNextAccum = 0;   // accumulation past dwlAccum
            int     iData;              // which UI4 in *This we are on.

            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4}; 

            for (iData = 0; iData < iDataMax; iData++) { 
                SQLDebug.Check(dwlAccum < x_ulInt32Base); 

                ulong ulTemp = (ulong)rguiData[iData]; 
                dwlNextAccum = ulTemp * (ulong)uiMultiplier;
                dwlAccum += dwlNextAccum;
                if (dwlAccum < dwlNextAccum)        // Overflow of int64 add
                    dwlNextAccum = x_ulInt32Base;   // how much to add to dwlAccum after div x_dwlBaseUI4 
                else
                    dwlNextAccum = 0; 
                rguiData[iData] = (uint)dwlAccum;           // equivalent to mod x_dwlBaseUI4 
                dwlAccum = (dwlAccum >> 32) + dwlNextAccum; // equivalent to div x_dwlBaseUI4
            } 

            // If any carry,
            if (dwlAccum != 0) {
                // Either overflowed 
                SQLDebug.Check(dwlAccum < x_ulInt32Base, "dwlAccum < x_dwlBaseUI4", "Integer overflow");
                if (iDataMax == x_cNumeMax) 
                    throw new OverflowException(SQLResource.ArithOverflowMessage); 

                // Or extend length by one uint 
                rguiData[iDataMax] = (uint)dwlAccum;
                m_bLen ++;
            }
 
            if (FGt10_38 (rguiData))
                throw new OverflowException(SQLResource.ArithOverflowMessage); 
 
            StoreFromWorkingArray(rguiData);
        } 


        //    DivByULong()
        // 
        //    Divide numeric value by a ULONG.  The result will be returned
        //    in the dividend *this. 
        // 
        //    Parameters:
        //        this        - IN Dividend & OUT Result 
        //        ulDivisor    - IN Divisor
        //    Returns:        - OUT Remainder
        //
        private uint DivByULong(uint iDivisor) { 
            ulong   dwlDivisor = (ulong) iDivisor;
            ulong   dwlAccum = 0;           //Accumulated sum 
            uint    ulQuotientCur = 0;      // Value of the current UI4 of the quotient 
            bool    fAllZero = true;    // All of the quotient (so far) has been 0
            int     iData;              //Which UI4 currently on 

            // Check for zero divisor.
            if (dwlDivisor == 0)
                throw new DivideByZeroException(SQLResource.DivideByZeroMessage); 

            // Copy into array, so that we can iterate through the data 
            uint[] rguiData = new uint[4] { m_data1, m_data2, m_data3, m_data4}; 

            // Start from the MS UI4 of quotient, divide by divisor, placing result 
            //        in quotient and carrying the remainder.
            //DEVNOTE DWORDLONG sufficient accumulator since:
            //        Accum < Divisor <= 2^32 - 1    at start each loop
            //                                    initially,and mod end previous loop 
            //        Accum*2^32 < 2^64 - 2^32
            //                                    multiply both side by 2^32 (x_dwlBaseUI4) 
            //        Accum*2^32 + m_rgulData < 2^64 
            //                                    rglData < 2^32
            for (iData = m_bLen; iData > 0; iData--) { 
                SQLDebug.Check(dwlAccum < dwlDivisor);
                dwlAccum = (dwlAccum << 32) + (ulong)(rguiData[iData-1]); // dwlA*x_dwlBaseUI4 + rglData
                SQLDebug.Check((dwlAccum / dwlDivisor) < x_ulInt32Base);
                //Update dividend to the quotient. 
                ulQuotientCur = (uint)(dwlAccum / dwlDivisor);
                rguiData[iData-1] = ulQuotientCur; 
                //Remainder to be carried to the next lower significant byte. 
                dwlAccum = dwlAccum % dwlDivisor;
 
                // While current part of quotient still 0, reduce length
                if (fAllZero && (ulQuotientCur == 0)) {
                    m_bLen --;
                } else { 
                    fAllZero = false;
                } 
            } 

            StoreFromWorkingArray(rguiData); 

            // If result is 0, preserve sign but set length to 5
            if (fAllZero)
                m_bLen = 1; 

            AssertValid(); 
 
            // return the remainder
            SQLDebug.Check(dwlAccum < x_ulInt32Base); 
            return(uint)dwlAccum;
        }

        //    AdjustScale() 
        //
        //    Adjust number of digits to the right of the decimal point. 
        //    A positive adjustment increases the scale of the numeric value 
        //    while a negative adjustment decreases the scale.  When decreasing
        //    the scale for the numeric value, the remainder is checked and 
        //    rounded accordingly.
        //
        internal void AdjustScale(int digits, bool fRound) {
            SQLDebug.Check(!IsNull, "!IsNull", "In AdjustScale"); 

            uint    ulRem;                  //Remainder when downshifting 
            uint    ulShiftBase;            //What to multiply by to effect scale adjust 
            bool    fNeedRound = false;     //Do we really need to round?
            byte    bNewScale, bNewPrec; 
            int     lAdjust = digits;

            //If downshifting causes truncation of data
            if (lAdjust + m_bScale < 0) 
                throw new SqlTruncateException();
 
            //If uphifting causes scale overflow 
            if (lAdjust + m_bScale > NUMERIC_MAX_PRECISION)
                throw new OverflowException(SQLResource.ArithOverflowMessage); 

            bNewScale = (byte) (lAdjust + m_bScale);
            bNewPrec = (byte) (Math.Min(NUMERIC_MAX_PRECISION,Math.Max(1,lAdjust + m_bPrec)));
 
            if (lAdjust > 0) {
                m_bScale = bNewScale; 
                m_bPrec  = bNewPrec; 

                while (lAdjust > 0) { 
                    //if lAdjust>=9, downshift by 10^9 each time, otherwise by the full amount
                    if (lAdjust >= 9) {
                        ulShiftBase = x_rgulShiftBase[8];
                        lAdjust -= 9; 
                    }
                    else { 
                        ulShiftBase = x_rgulShiftBase[lAdjust-1]; 
                        lAdjust = 0;
                    } 
                    MultByULong(ulShiftBase);
                }
            }
            else if (lAdjust < 0) { 
                do {
                    if (lAdjust <= -9) { 
                        ulShiftBase = x_rgulShiftBase[8]; 
                        lAdjust += 9;
                    } 
                    else {
                        ulShiftBase = x_rgulShiftBase[-lAdjust - 1];
                        lAdjust = 0;
                    } 
                    ulRem = DivByULong (ulShiftBase);
                } 
                while (lAdjust < 0); 

                // Do we really need to round? 
                fNeedRound = (ulRem >= ulShiftBase/2);

                m_bScale = bNewScale;
                m_bPrec =  bNewPrec; 
            }
 
            AssertValid(); 

            // After adjusting, if the result is 0 and remainder is less than 5, 
            // set the sign to be positive and return.
            if (fNeedRound && fRound) {
                // If remainder is 5 or above, increment/decrement by 1.
                AddULong(1); 
            }
            else if (FZero()) 
                SetPositive(); 
        }
 
        // Adjust scale of a SqlDecimal
        /// 
        ///    [To be supplied.]
        ///  
        public static SqlDecimal AdjustScale(SqlDecimal n, int digits, bool fRound) {
            if (n.IsNull) 
                return SqlDecimal.Null; 

            SqlDecimal ret = n; 
            ret.AdjustScale(digits, fRound);
            return ret;
        }
 
        // Convert to a specific precision and scale
        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal ConvertToPrecScale(SqlDecimal n, int precision, int scale) { 
            CheckValidPrecScale(precision, scale);
            n.AssertValid();

            if (n.IsNull) 
                return SqlDecimal.Null;
 
            SqlDecimal ret = n; 

            int lPrecAdjust = precision - (int)ret.m_bPrec;//Adjustment to precision 
            int lScaleAdjust = scale - (int)ret.m_bScale;//Adjustment to scale

            //Adjust scale
            ret.AdjustScale(lScaleAdjust, true); 

// devnote: is that still true? 
            //Shouldn't truncate the integer digits; BActualPrec() is an expensive 
            //function call, so test the data length first to eliminate majority
            //of cases 

            //The number of bytes storage required by the new precision
            byte cbWithNewPrec = CLenFromPrec((byte)precision);
 
            if (cbWithNewPrec < ret.m_bLen) {
                //if current actual length greater than length corresponding to bNewPrec 
                //there must be truncating 
                throw new SqlTruncateException();
            } 
            else if (cbWithNewPrec == ret.m_bLen) {
                //if the two lengths equal, need to check the actual precision
                if (precision < ret.CalculatePrecision())
                    throw new SqlTruncateException(); 
            }
 
            //Adjust precision 
            ret.m_bPrec = (byte)precision;
 
            ret.AssertValid();

            return ret;
        } 

        //    LAbsCmp() 
        // 
        //    Compare the absolute value of two numerics without checking scale
        // 
        //  Parameters:
        //        this    - IN Operand1
        //        snumOp    - IN Operand2
        // 
        //    Returns:
        //        positive    - |this| > |snumOp| 
        //        0            - |this| = |snumOp| 
        //        negative    - |this| < |snumOp|
        // 
        private int LAbsCmp(SqlDecimal snumOp) {

            int         iData;  //which UI4 we are operating on
            int         culOp;  //#of UI4s on operand 
            int         culThis; //# of UI4s in this
 
            // If one longer, it is larger 
            culOp = snumOp.m_bLen;
            culThis = m_bLen; 
            if (culOp != culThis)
                return(culThis > culOp) ? 1 : -1;

            uint[] rglData1 = new uint[4] {m_data1, m_data2, m_data3, m_data4}; 
            uint[] rglData2 = new uint[4] {snumOp.m_data1, snumOp.m_data2, snumOp.m_data3, snumOp.m_data4};
 
            // Loop through numeric value checking each byte for differences. 
            iData = culOp-1;
            do { 
                //

                if (rglData1[iData] != rglData2[iData])
                    return((rglData1[iData] > rglData2[iData]) ? 1 : -1); 
                iData --;
            } 
            while (iData >= 0); 

            // All UI4s the same, return 0. 
            return 0;
        }

        // Move multi-precision number 
        private static void MpMove
        ( 
        uint[]  rgulS,      // In    | Source number 
        int     ciulS,      // In    | # of digits in S
        uint[]  rgulD,      // Out    | Destination number 
        out int ciulD       // Out    | # of digits in D
        ) {
            ciulD = ciulS;
 
            SQLDebug.Check(rgulS.Length >= ciulS, "rgulS.Length >= ciulS", "Invalid array length");
            SQLDebug.Check(rgulD.Length >= ciulS, "rgulD.Length >= ciulS", "Invalid array length"); 
 
            for (int i = 0; i < ciulS; i ++)
                rgulD[i] = rgulS[i]; 
        }

        // Set multi-precision number to one super-digit
        private static void MpSet 
        (
        uint[]  rgulD,      // Out    | Number 
        out int ciulD,      // Out    | # of digits in D 
        uint    iulN        // In    | ULONG to set
        ) { 
            ciulD = 1;
            rgulD[0] = iulN;
        }
 
        // Normalize multi-precision number - remove leading zeroes
        private static void MpNormalize 
        ( 
        uint[]  rgulU,      // In   | Number
        ref int ciulU       // InOut| # of digits 
        ) {
            while (ciulU > 1 && rgulU[ciulU-1] == 0)
                ciulU --;
        } 

        // Multi-precision one super-digit multiply in place. 
        // D = D * X 
        // Length can increase
        private static void MpMul1 
        (
        uint[]  piulD,      // InOut| D
        ref int ciulD,      // InOut| # of digits in D
        uint    iulX        // In    | X 
        ) {
            SQLDebug.Check(iulX > x_uiZero); 
            uint ulCarry = 0; 
            int iData;
            ulong dwlAccum; 

            for (iData = 0; iData < ciulD; iData ++) {
                ulong ulTemp = (ulong)piulD[iData];
                dwlAccum = ulCarry + ulTemp * (ulong)iulX; 
                ulCarry = HI (dwlAccum);
                piulD[iData] = LO (dwlAccum); 
            } 

            // If overflow occurs, increase precision 
            if (ulCarry != 0) {
                piulD[iData] = ulCarry;
                ciulD ++;
            } 
        }
 
        // Multi-precision one super-digit divide in place. 
        // U = U / D,
        // R = U % D 
        // Length of U can decrease
        private static void MpDiv1
        (
        uint[]      rgulU,      // InOut| U 
        ref int     ciulU,      // InOut| # of digits in U
        uint        iulD,       // In    | D 
        out uint    iulR        // Out    | R 
        ) {
            SQLDebug.Check(rgulU.Length == x_cNumeMax); 

            uint    ulCarry = 0;
            ulong   dwlAccum;
            ulong   ulD = (ulong)iulD; 
            int     idU = ciulU;
 
            SQLDebug.Check(iulD != 0, "iulD != 0", "Divided by zero!"); 
            SQLDebug.Check(iulD > 0, "iulD > 0", "Invalid data: less than zero");
            SQLDebug.Check(ciulU > 0, "ciulU > 0", "No data in the array"); 

            while (idU > 0) {
                idU --;
                dwlAccum = (((ulong)ulCarry) << 32) + (ulong)(rgulU[idU]); 
                rgulU[idU] = (uint)(dwlAccum / ulD);
                ulCarry = (uint) (dwlAccum - (ulong)rgulU[idU] * ulD);  // (ULONG) (dwlAccum % iulD) 
            } 
            iulR = ulCarry;
            MpNormalize (rgulU, ref ciulU); 
        }

        internal static ulong DWL(uint lo, uint hi) {
            return(ulong)lo + ( ((ulong)hi) << 32 ); 
        }
 
        private static uint HI(ulong x) { 
            return(uint)(x >> 32);
        } 

        private static uint LO(ulong x) {
            return(uint)x;
        } 

        // Multi-precision divide. 
        // Q = U / D, 
        // R = U % D,
        // U and D not changed. 
        // It is Ok for U and R to have the same location in memory,
        // but then U will be changed.
        // Assumes that there is enough room in Q and R for results.
 
        // Drawbacks of this implementation:
        //    1)    Need one extra super-digit (ULONG) in R 
        //    2)    As it modifies D during work, then it do (probably) unnecessary 
        //        work to restore it
        //    3)    Always get Q and R - if R is unnecessary, can be slightly faster 
        // Most of this can be fixed if it'll be possible to have a working buffer. But
        // then we'll use alloca() or there will be limit on the upper size of numbers
        // (maybe not a problem in SQL Server).
        // 
        private static void MpDiv
        ( 
        uint[]  rgulU,      // In    | U 
        int     ciulU,      // In    | # of digits in U
        uint[]  rgulD,      // In    | D 
        int     ciulD,      // In    | # of digits in D
        uint[]  rgulQ,      // Out    | Q
        out int ciulQ,      // Out    | # of digits in Q
        uint[]  rgulR,      // Out    | R 
        out int ciulR       // Out    | # of digits in R
        ) { 
            SQLDebug.Check(ciulU > 0, "ciulU > 0", "In method MpDiv"); 
            SQLDebug.Check(ciulD > 0, "ciulD > 0", "In method MpDiv");
            SQLDebug.Check(rgulU.Length == x_cNumeMax); 
            SQLDebug.Check(rgulD.Length == x_cNumeMax);

            // Division by zero?
            if (ciulD == 1 && rgulD[0] == 0) { 
                ciulQ = ciulR = 0;
            } 
 
            // Check for simplest case, so it'll be fast
            else if (ciulU == 1 && ciulD == 1) { 
                MpSet (rgulQ, out ciulQ, rgulU[0] / rgulD[0]);
                MpSet (rgulR, out ciulR, rgulU[0] % rgulD[0]);
            }
 
            // If D > U then do not divide at all
            else if (ciulD > ciulU) { 
                MpMove (rgulU, ciulU, rgulR, out ciulR);        // R = U 
                MpSet (rgulQ, out ciulQ, 0);                    // Q = 0
            } 

            // Try to divide faster - check for remaining good sizes (8 / 4, 8 / 8)
            else if (ciulU <= 2) {
                ulong dwlU, dwlD, dwlT; 

                dwlU = DWL (rgulU[0], rgulU[1]); 
                dwlD = rgulD[0]; 
                if (ciulD > 1)
                    dwlD += ( ((ulong)rgulD[1]) << 32 ); 
                dwlT = dwlU / dwlD;
                rgulQ[0] = LO (dwlT);
                rgulQ[1] = HI (dwlT);
                ciulQ = (HI (dwlT) != 0) ? 2 : 1; 
                dwlT = dwlU % dwlD;
                rgulR[0] = LO (dwlT); 
                rgulR[1] = HI (dwlT); 
                ciulR = (HI (dwlT) != 0) ? 2 : 1;
            } 

            // If we are dividing by one digit - use simpler routine
            else if (ciulD == 1) {
                MpMove (rgulU, ciulU, rgulQ, out ciulQ);        // Q = U 
                uint remainder;
                MpDiv1 (rgulQ, ref ciulQ, rgulD[0], out remainder);     // Q = Q / D, R = Q % D 
                rgulR[0] = remainder; 
                ciulR = 1;
            } 

            // Worst case. Knuth, "The Art of Computer Programming", 3rd edition, vol.II, Alg.D, pg 272
            else {
                ciulQ = ciulR = 0; 

                uint D1, ulDHigh, ulDSecond; 
                int iulRindex; 

                if (rgulU != rgulR) 
                    MpMove (rgulU, ciulU, rgulR, out ciulR);        // R = U

                ciulQ = ciulU - ciulD + 1;
                ulDHigh = rgulD[ciulD-1]; 

                // D1.    Normalize so high digit of D >= BASE/2 - that guarantee 
                //        that QH will not be too far from the correct digit later in D3 
                rgulR[ciulU] = 0;
                iulRindex = ciulU; 
                D1 = (uint)(x_ulInt32Base / ((ulong)ulDHigh + 1));
                if (D1 > 1) {
                    MpMul1 (rgulD, ref ciulD, D1);
                    ulDHigh = rgulD[ciulD-1]; 
                    MpMul1 (rgulR, ref ciulR, D1);
                } 
                ulDSecond = rgulD[ciulD-2]; 
                // D2 already done - iulRindex initialized before normalization of R.
                // D3-D7. Loop on iulRindex - obtaining digits one-by-one, as "in paper" 
                do {
                    uint QH, RH;
                    int iulDindex, iulRwork;
                    ulong dwlAccum, dwlMulAccum; 

                    // D3. Calculate Q hat - estimation of the next digit 
                    dwlAccum = DWL (rgulR[iulRindex-1], rgulR[iulRindex]); 
                    if (ulDHigh == rgulR[iulRindex])
                        QH = (uint)(x_ulInt32Base - 1); 
                    else
                        QH = (uint)(dwlAccum / ulDHigh);
                    ulong ulTemp = (ulong)QH;
                    RH = (uint)(dwlAccum - ulTemp * (ulong)ulDHigh); 

                    while (ulDSecond * ulTemp > DWL (rgulR[iulRindex-2], RH)) { 
                        QH --; 
                        if (RH >= (uint) - ((int) ulDHigh))
                            break; 
                        RH += ulDHigh;
                        ulTemp = (ulong)QH;
                    }
 
                    // D4. Multiply and subtract: (some digits of) R -= D * QH
                    for (dwlAccum = x_ulInt32Base, dwlMulAccum = 0, iulDindex = 0, iulRwork = iulRindex - ciulD; 
                        iulDindex < ciulD; iulDindex ++, iulRwork ++) { 
                        ulong ulTemp2 = (ulong)rgulD[iulDindex];
                        dwlMulAccum += (ulong)QH * ulTemp2; 
                        dwlAccum += (ulong)rgulR[iulRwork] - LO (dwlMulAccum);
                        dwlMulAccum = (ulong)(HI (dwlMulAccum));
                        rgulR[iulRwork] = LO (dwlAccum);
                        dwlAccum = HI (dwlAccum) + x_ulInt32Base - 1; 
                    }
                    dwlAccum += (ulong)rgulR[iulRwork] - dwlMulAccum; 
                    rgulR[iulRwork] = LO (dwlAccum); 
                    rgulQ[iulRindex-ciulD] = QH;
 
                    // D5. Test remainder. Carry indicates result<0, therefore QH 1 too large
                    if (HI (dwlAccum) == 0) {
                        // D6. Add back - probabilty is 2**(-31). R += D. Q[digit] -= 1
                        uint ulCarry; 

                        rgulQ[iulRindex-ciulD] = QH - 1; 
                        for (ulCarry = 0, iulDindex = 0, iulRwork = iulRindex - ciulD; 
                            iulDindex < ciulD; iulDindex ++, iulRwork ++) {
                            dwlAccum = (ulong)rgulD[iulDindex] + (ulong)rgulR[iulRwork] + (ulong)ulCarry; 
                            ulCarry = HI (dwlAccum);
                            rgulR[iulRwork] = LO (dwlAccum);
                        }
                        rgulR[iulRwork] += ulCarry; 
                    }
                    // D7. Loop on iulRindex 
                    iulRindex --; 
                }
                while (iulRindex >= ciulD); 
                // Normalize results
                MpNormalize (rgulQ, ref ciulQ);
                ciulR = ciulD;
                MpNormalize (rgulR, ref ciulR); 
                // D8. Unnormalize: Divide D and R to get result
                if (D1 > 1) { 
                    uint ret; 
                    MpDiv1 (rgulD, ref ciulD, D1, out ret);
                    MpDiv1 (rgulR, ref ciulR, D1, out ret); 
                }
            }
        }
 
        //    CmpCompareNm()
        // 
        //    Compare the value of two numerics 
        //
        //    Complexity: O(pn) p: precision  n: length 
        //
        //  Parameters:
        //        this    - IN Operand1
        //        snumOp    - IN operand2 
        //
        //    Returns: 
        //        EComparison.LT    - this < snumOp 
        //        EComparison.EQ        - this = snumOp
        //        EComparison.GT    - this > snumOp 
        //
        private EComparison CompareNm
        (
        SqlDecimal snumOp 
        ) {
            AssertValid(); 
            snumOp.AssertValid(); 

            //Signs of the two numeric operands 
            int     Sign1;
            int     Sign2;

            int     iFinalResult;   //Final result of comparision: positive = greater 
                                    //than, 0 = equal, negative = less than
 
            //Initialize the sign values to be 1(positive) or -1(negative) 
            Sign1 = IsPositive ? 1 : -1;
            Sign2 = snumOp.IsPositive ? 1 : -1; 

            if (Sign1 != Sign2) //If different sign, the positive one is greater
                return Sign1 == 1 ? EComparison.GT : EComparison.LT;
 
            else { //same sign, have to compare absolute values
                //Temporary memory to hold the operand since it is const 
                //but its scale may get adjusted during comparison 
                int         ScaleDiff;
                SqlDecimal  snumArg1 = this; 
                SqlDecimal  snumArg2 = snumOp;

                //First make the two operands the same scale if necessary
                ScaleDiff = ((int) m_bScale) - ((int) snumOp.m_bScale); 

                if (ScaleDiff < 0) { 
                    //If increasing the scale of operand1 caused an overflow, 
                    //then its absolute value is greater than that of operand2.
                    try { 
                        snumArg1.AdjustScale(-ScaleDiff, true);
                    }
                    catch (OverflowException) {
                        return(Sign1 > 0) ? EComparison.GT : EComparison.LT; 
                    }
                } 
                else if (ScaleDiff > 0) { 
                    //If increasing the scale of operand2 caused an overflow, then
                    //operand1's absolute value is less than that of operand2. 
                    try {
                        snumArg2.AdjustScale(ScaleDiff, true);
                    }
                    catch (OverflowException) { 
                        return(Sign1 > 0) ? EComparison.LT : EComparison.GT;
                    } 
                } 

                //Compare the absolute value of the two numerics 
                //Note: We are sure that scale of arguments is the same,
                //      so LAbsCmp() will not modify its argument.
                int lResult = snumArg1.LAbsCmp(snumArg2);
                if (0 == lResult) 
                    return EComparison.EQ;
 
                //if both positive, result same as result from LAbsCmp; 
                //if both negative, result reverse of result from LAbsCmp
                iFinalResult = Sign1 * lResult; 

                if (iFinalResult < 0)
                    return EComparison.LT;
                else 
                    return EComparison.GT;
            } 
        } 

        private static void CheckValidPrecScale(byte bPrec, byte bScale) { 
            if (bPrec < 1 || bPrec > MaxPrecision || bScale < 0 || bScale > MaxScale || bScale > bPrec)
                throw new SqlTypeException(SQLResource.InvalidPrecScaleMessage);
        }
 
        private static void CheckValidPrecScale(int iPrec, int iScale) {
            if (iPrec < 1 || iPrec > MaxPrecision || iScale < 0 || iScale > MaxScale || iScale > iPrec) 
                throw new SqlTypeException(SQLResource.InvalidPrecScaleMessage); 
        }
 
        // Overloading comparison operators
        /// 
        ///    [To be supplied.]
        ///  
        public static SqlBoolean operator==(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.EQ); 
        } 

        ///  
        ///    [To be supplied.]
        /// 
        public static SqlBoolean operator!=(SqlDecimal x, SqlDecimal y) {
            return ! (x == y); 
        }
 
        ///  
        ///    [To be supplied.]
        ///  
        public static SqlBoolean operator<(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.LT);
        }
 
        /// 
        ///    [To be supplied.] 
        ///  
        public static SqlBoolean operator>(SqlDecimal x, SqlDecimal y) {
            return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.CompareNm(y) == EComparison.GT); 
        }

        /// 
        ///    [To be supplied.] 
        /// 
        public static SqlBoolean operator<=(SqlDecimal x, SqlDecimal y) { 
            if (x.IsNull || y.IsNull) 
                return SqlBoolean.Null;
            else { 
                EComparison result = x.CompareNm(y);
                return new SqlBoolean(result == EComparison.LT || result == EComparison.EQ);
            }
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlBoolean operator>=(SqlDecimal x, SqlDecimal y) { 
            if (x.IsNull || y.IsNull)
                return SqlBoolean.Null;
            else {
                EComparison result = x.CompareNm(y); 
                return new SqlBoolean(result == EComparison.GT || result == EComparison.EQ);
            } 
        } 

 
        //--------------------------------------------------
        // Alternative methods for overloaded operators
        //--------------------------------------------------
 
        // Alternative method for operator +
        public static SqlDecimal Add(SqlDecimal x, SqlDecimal y) { 
            return x + y; 
        }
        // Alternative method for operator - 
        public static SqlDecimal Subtract(SqlDecimal x, SqlDecimal y) {
            return x - y;
        }
 
        // Alternative method for operator *
        public static SqlDecimal Multiply(SqlDecimal x, SqlDecimal y) { 
            return x * y; 
        }
 
        // Alternative method for operator /
        public static SqlDecimal Divide(SqlDecimal x, SqlDecimal y) {
            return x / y;
        } 

        // Alternative method for operator == 
        public static SqlBoolean Equals(SqlDecimal x, SqlDecimal y) { 
            return (x == y);
        } 

        // Alternative method for operator !=
        public static SqlBoolean NotEquals(SqlDecimal x, SqlDecimal y) {
            return (x != y); 
        }
 
        // Alternative method for operator < 
        public static SqlBoolean LessThan(SqlDecimal x, SqlDecimal y) {
            return (x < y); 
        }

        // Alternative method for operator >
        public static SqlBoolean GreaterThan(SqlDecimal x, SqlDecimal y) { 
            return (x > y);
        } 
 
        // Alternative method for operator <=
        public static SqlBoolean LessThanOrEqual(SqlDecimal x, SqlDecimal y) { 
            return (x <= y);
        }

        // Alternative method for operator >= 
        public static SqlBoolean GreaterThanOrEqual(SqlDecimal x, SqlDecimal y) {
            return (x >= y); 
        } 

        // Alternative method for conversions. 

        public SqlBoolean ToSqlBoolean() {
            return (SqlBoolean)this;
        } 

        public SqlByte ToSqlByte() { 
            return (SqlByte)this; 
        }
 
        public SqlDouble ToSqlDouble() {
            return (SqlDouble)this;
        }
 
        public SqlInt16 ToSqlInt16() {
            return (SqlInt16)this; 
        } 

        public SqlInt32 ToSqlInt32() { 
            return (SqlInt32)this;
        }

        public SqlInt64 ToSqlInt64() { 
            return (SqlInt64)this;
        } 
 
        public SqlMoney ToSqlMoney() {
            return (SqlMoney)this; 
        }

        public SqlSingle ToSqlSingle() {
            return (SqlSingle)this; 
        }
 
        public SqlString ToSqlString() { 
            return (SqlString)this;
        } 

        private static char ChFromDigit(uint uiDigit) {
            SQLDebug.Check(uiDigit < 10);
            return(char)(uiDigit + '0'); 
        }
 
        // Store data back from rguiData[] to m_data* 
        private void StoreFromWorkingArray(uint[] rguiData) {
            SQLDebug.Check(rguiData.Length == 4); 
            m_data1 = rguiData[0];
            m_data2 = rguiData[1];
            m_data3 = rguiData[2];
            m_data4 = rguiData[3]; 
        }
 
        private void SetToZero() { 
            SQLDebug.Check(m_bPrec >= 1);
            m_bLen = 1; 
            m_data1 =
            m_data2 =
            m_data3 =
            m_data4 = 0; 
            m_bStatus = (byte) (x_bNotNull | x_bPositive);
            AssertValid(); 
        } 

        // Truncate to integer 
        private void MakeInteger(out bool fFraction) {
            uint ulRem;
            int iAdjust = m_bScale;
 
            fFraction = false;
 
            while (iAdjust > 0) { 
                if (iAdjust >= 9) {
                    ulRem = DivByULong (x_rgulShiftBase[8]); 
                    iAdjust -= 9;
                }
                else {
                    ulRem = DivByULong (x_rgulShiftBase[iAdjust-1]); 
                    iAdjust = 0;
                } 
 
                // Check for remainder and set fFraction flag.
                if (ulRem != 0) 
                    fFraction = true;
            }

            m_bScale = 0; 
            AssertValid();
        } 
 
        // Builtin functions
 
        // Abs - absolute value
        /// 
        ///    [To be supplied.]
        ///  
        public static SqlDecimal Abs(SqlDecimal n) {
            n.AssertValid(); 
 
            if (n.IsNull)
                return SqlDecimal.Null; 

            n.SetPositive();
            n.AssertValid();
            return n; 
        }
 
        // Ceiling - next smallest integer greater than or equal to the numeric 
        /// 
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal Ceiling(SqlDecimal n) {
            n.AssertValid();
 
            if (n.IsNull)
                return SqlDecimal.Null; 
 
            if (n.m_bScale == 0)
                return n; 

            bool fFraction;    //Fractional flag

            n.MakeInteger(out fFraction); 

            //When the numeric has fraction and is positive, adjust by adding 1. 
            //Otherwise return the integral part. 
            if (fFraction && n.IsPositive) {
                n.AddULong(1); 
            }

            if (n.FZero())//if result is zero, sign should be positive
                n.SetPositive(); 
            n.AssertValid();
            return n; 
        } 

        // Floor - next largest integer smaller or equal to the numeric 
        /// 
        ///    [To be supplied.]
        /// 
        public static SqlDecimal Floor(SqlDecimal n) { 
            n.AssertValid();
 
            if (n.IsNull) 
                return SqlDecimal.Null;
 
            if (n.m_bScale == 0)
                return n;

            bool fFraction;    //Fractional flag 

            n.MakeInteger(out fFraction); 
 
            //When the numeric has fraction and is negative, subtract 1 by calling AddULong(1)
            //Otherwise return the integral part. 
            if (fFraction && !n.IsPositive) {
                n.AddULong(1);
            }
 
            if (n.FZero())//if result is zero, sign should be positive
                n.SetPositive(); 
            n.AssertValid(); 
            return n;
        } 

        // Sign -   1 if positive, -1 if negative
        /// 
        ///    [To be supplied.] 
        /// 
        public static SqlInt32 Sign(SqlDecimal n) { 
            n.AssertValid(); 

            if (n.IsNull) 
                return SqlInt32.Null;

            if (n == new SqlDecimal(0))
                return SqlInt32.Zero; 
            else
                return n.IsNull ? SqlInt32.Null : 
                    (n.IsPositive ? new SqlInt32(1) : new SqlInt32(-1)); 
        }
 
        private static SqlDecimal Round(SqlDecimal n, int lPosition, bool fTruncate) {
            if (n.IsNull)
                return SqlDecimal.Null;
 
            if (lPosition >= 0) {
                //If round to the right of decimal number 
                lPosition = Math.Min(NUMERIC_MAX_PRECISION, lPosition); 
                if (lPosition >= n.m_bScale)
                    return n;   //No need to round 
            }
            else {
                //If round to the left of the decimal point
                lPosition = Math.Max(-NUMERIC_MAX_PRECISION, lPosition); 

                //Return +0.00 if truncation of integer part 
                if (lPosition < n.m_bScale - n.m_bPrec) { 
                    n.SetToZero();
                    return n; 
                }
            }

            uint ulRem = 0;                                         // Remainder: the highest significant digit to be truncated 
            int lAdjust = Math.Abs(lPosition - (int)n.m_bScale);    // Precision adjustment
            uint ulLastDivBase = 1;                                 // 
 
            //Compute the integral part of the numeric
            while (lAdjust > 0) { 
                if (lAdjust >= 9) {
                    ulRem = n.DivByULong (x_rgulShiftBase[8]);
                    ulLastDivBase = x_rgulShiftBase[8];
                    lAdjust -= 9; 
                }
                else { 
                    ulRem = n.DivByULong (x_rgulShiftBase[lAdjust-1]); 
                    ulLastDivBase = x_rgulShiftBase[lAdjust-1];
                    lAdjust = 0; 
                }
            }

            // The rounding only depends on the first digit after the rounding position 
            if (ulLastDivBase > 1) {
                ulRem /= (ulLastDivBase / 10); 
            } 

            //If result is zero, return 
            if (n.FZero() && (fTruncate || ulRem < 5))
                {
                n.SetPositive();
                n.AssertValid(); 
                return n;
                } 
 
            // Adjust by adding 1 if remainder is larger than 5
            if (ulRem >= 5 && !fTruncate) 
                n.AddULong(1);

            // Convert back to original scale
            lAdjust = Math.Abs(lPosition - n.m_bScale); 

            while (lAdjust-- > 0) { 
                n.MultByULong(x_ulBase10); 
            }
 
            n.AssertValid();
            return n;
        }
 
        // Round - Round the numeric to a specific digit
        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal Round(SqlDecimal n, int position) { 
            n.AssertValid();
            return Round(n, position, false);
        }
 
        // Truncate - Truncate the numeric to a specific digit
        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal Truncate(SqlDecimal n, int position) { 
            n.AssertValid();
            return Round(n, position, true);
        }
 
        // Power - Compute the power of a numeric
        ///  
        ///    [To be supplied.] 
        /// 
        public static SqlDecimal Power(SqlDecimal n, double exp) { 
            n.AssertValid();

            if (n.IsNull)
                return SqlDecimal.Null; 

            byte prec = n.Precision; 
            int scale = n.Scale; 
            double dBaseNum = n.ToDouble();
 
            n = new SqlDecimal(Math.Pow(dBaseNum, exp));
            n.AdjustScale(scale - (int)n.Scale, true);

            n.m_bPrec = MaxPrecision; 

            return n; 
        } 

 
        // IComparable
        // Compares this object to another object, returning an integer that
        // indicates the relationship.
        // Returns a value less than zero if this < object, zero if this = object, 
        // or a value greater than zero if this > object.
        // null is considered to be less than any instance. 
        // If object is not of same type, this method throws an ArgumentException. 
        /// 
        ///    [To be supplied.] 
        /// 
        public int CompareTo(Object value) {
            if (value is SqlDecimal) {
                SqlDecimal i = (SqlDecimal)value; 

                return CompareTo(i); 
            } 
            throw ADP.WrongType(value.GetType(), typeof(SqlDecimal));
        } 

        public int CompareTo(SqlDecimal value) {
            // If both Null, consider them equal.
            // Otherwise, Null is less than anything. 
            if (IsNull)
                return value.IsNull ? 0  : -1; 
            else if (value.IsNull) 
                return 1;
 
            if (this < value) return -1;
            if (this > value) return 1;
            return 0;
        } 

        // Compares this instance with a specified object 
        ///  
        ///    [To be supplied.]
        ///  
        public override bool Equals(Object value) {
            if (!(value is SqlDecimal)) {
                return false;
            } 

            SqlDecimal i = (SqlDecimal)value; 
 
            if (i.IsNull || IsNull)
                return (i.IsNull && IsNull); 
            else
                return (this == i).Value;
        }
 
        // For hashing purpose
        ///  
        ///    [To be supplied.] 
        /// 
        public override int GetHashCode() { 
            if (IsNull)
                return 0;

            SqlDecimal ssnumTemp; 
            int        lActualPrec;
 
            // First, "normalize" numeric, so that values with different 
            // scale/precision will have the same representation.
            ssnumTemp = this; 
            lActualPrec = ssnumTemp.CalculatePrecision();
            ssnumTemp.AdjustScale(NUMERIC_MAX_PRECISION - lActualPrec, true);

            // Now evaluate the hash 
            int cDwords = ssnumTemp.m_bLen;
            int ulValue = 0; 
            int ulHi; 

            // Size of CRC window (hashing bytes, ssstr, sswstr, numeric) 
            const int x_cbCrcWindow = 4;
            // const int iShiftVal = (sizeof ulValue) * (8*sizeof(char)) - x_cbCrcWindow;
            const int iShiftVal = 4 * 8 - x_cbCrcWindow;
 
            int[] rgiData = ssnumTemp.Data;
 
            for (int i = 0; i < cDwords; i++) 
                {
                ulHi = (ulValue >> iShiftVal) & 0xff; 
                ulValue <<= x_cbCrcWindow;
                ulValue = ulValue ^ rgiData[i] ^ ulHi;
                }
            return ulValue; 
        }
 
        ///  
        ///    [To be supplied.]
        ///  
        XmlSchema IXmlSerializable.GetSchema() { return null; }

        /// 
        ///    [To be supplied.] 
        /// 
        void IXmlSerializable.ReadXml(XmlReader reader) { 
            string isNull = reader.GetAttribute("nil", XmlSchema.InstanceNamespace); 
            if (isNull != null && XmlConvert.ToBoolean(isNull)) {
                m_bStatus = (byte) (x_bReverseNullMask & m_bStatus); 
            }
            else {
                SqlDecimal dec = Parse(reader.ReadElementString());
                this.m_bStatus = dec.m_bStatus; 
                this.m_bLen = dec.m_bLen;
                this.m_bPrec = dec.m_bPrec; 
                this.m_bScale = dec.m_bScale; 
                this.m_data1 = dec.m_data1;
                this.m_data2 = dec.m_data2; 
                this.m_data3 = dec.m_data3;
                this.m_data4 = dec.m_data4;
            }
        } 

        ///  
        ///    [To be supplied.] 
        /// 
        void IXmlSerializable.WriteXml(XmlWriter writer) { 
            if (IsNull) {
                writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
            }
            else { 
                writer.WriteString(ToString());
            } 
        } 

        ///  
        ///    [To be supplied.]
        /// 
        public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) {
            return new XmlQualifiedName("decimal", XmlSchema.Namespace); 
        }
 
        // These values are defined at last, because they need to call ctors, which use other 
        // constant values. Those constants must be defined before callint ctor.
 
        /// 
        ///    [To be supplied.]
        /// 
        public static readonly SqlDecimal Null = new SqlDecimal(true); 

        ///  
        ///    [To be supplied.] 
        /// 
        public static readonly SqlDecimal MinValue    = SqlDecimal.Parse("-99999999999999999999999999999999999999"); 
        /// 
        ///    [To be supplied.]
        /// 
        public static readonly SqlDecimal MaxValue    = SqlDecimal.Parse("99999999999999999999999999999999999999"); 

    } // SqlDecimal 
 
} // namespace System.Data.SqlTypes

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK