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 { ////// [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 ////// Represents a fixed precision and scale numeric value between -10 ////// -1 and 10 -1 to be stored in or retrieved from a database. /// /// 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.[To be supplied.] ///
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- AssemblyHelper.cs
- StringBuilder.cs
- DES.cs
- ToolboxComponentsCreatedEventArgs.cs
- TypeConverterHelper.cs
- ObjectStorage.cs
- ProcessHostConfigUtils.cs
- MouseBinding.cs
- CAGDesigner.cs
- FileDialog_Vista_Interop.cs
- WebServiceEnumData.cs
- ImportContext.cs
- Panel.cs
- DataGridViewTextBoxColumn.cs
- AsmxEndpointPickerExtension.cs
- ProfileEventArgs.cs
- InputScope.cs
- CodeCastExpression.cs
- FlagsAttribute.cs
- TCEAdapterGenerator.cs
- Style.cs
- RetrieveVirtualItemEventArgs.cs
- DebugControllerThread.cs
- SectionRecord.cs
- SqlAggregateChecker.cs
- RemoteHelper.cs
- XmlSchemaExporter.cs
- MergePropertyDescriptor.cs
- BasicViewGenerator.cs
- PrintPreviewControl.cs
- SapiInterop.cs
- Delegate.cs
- BufferedReadStream.cs
- DeflateStream.cs
- WindowsListViewScroll.cs
- TimeManager.cs
- PerformanceCounterLib.cs
- SecureStringHasher.cs
- GeometryCollection.cs
- DefaultHttpHandler.cs
- MDIControlStrip.cs
- PolyQuadraticBezierSegment.cs
- DocumentApplicationJournalEntry.cs
- FtpWebRequest.cs
- FixedPageStructure.cs
- ZoneLinkButton.cs
- DecoratedNameAttribute.cs
- TdsParameterSetter.cs
- SimpleHandlerBuildProvider.cs
- ScriptMethodAttribute.cs
- X500Name.cs
- TextRangeProviderWrapper.cs
- EtwTrackingBehaviorElement.cs
- WindowsRegion.cs
- TypeUtils.cs
- Model3D.cs
- ButtonColumn.cs
- securitycriticaldataformultiplegetandset.cs
- IsolatedStorage.cs
- GlyphCollection.cs
- ColumnResizeUndoUnit.cs
- ResponseStream.cs
- ToolStripScrollButton.cs
- StandardCommands.cs
- ObjectStateEntryOriginalDbUpdatableDataRecord.cs
- OleDbDataReader.cs
- DetailsViewDeletedEventArgs.cs
- TraceProvider.cs
- Ray3DHitTestResult.cs
- ExpressionPrefixAttribute.cs
- DecoderBestFitFallback.cs
- Vector.cs
- SystemIPGlobalStatistics.cs
- TypeValidationEventArgs.cs
- XNodeNavigator.cs
- ButtonFlatAdapter.cs
- DataControlImageButton.cs
- ScriptManager.cs
- SafeEventLogReadHandle.cs
- PrintPageEvent.cs
- XmlTextEncoder.cs
- StaticContext.cs
- EventWaitHandle.cs
- TextWriterTraceListener.cs
- DesignerSerializationVisibilityAttribute.cs
- XmlNamedNodeMap.cs
- DataGridViewUtilities.cs
- KnownTypesProvider.cs
- CustomCategoryAttribute.cs
- ClientTargetCollection.cs
- TextViewSelectionProcessor.cs
- MembershipPasswordException.cs
- documentsequencetextcontainer.cs
- IsolatedStorageException.cs
- WindowProviderWrapper.cs
- DesignerTransaction.cs
- GeometryValueSerializer.cs
- TileModeValidation.cs
- TextEditorDragDrop.cs
- CodeDomComponentSerializationService.cs