// Truncate - Truncate the numeric to a specific digit /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> public static SqlDecimal Truncate(SqlDecimal n, int position) { n.AssertValid(); return Round(n, position, true); }
// Power - Compute the power of a numeric /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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; }
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(s_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(-s_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(s_rgulShiftBase[8]); ulLastDivBase = s_rgulShiftBase[8]; lAdjust -= 9; } else { ulRem = n.DivByULong(s_rgulShiftBase[lAdjust - 1]); ulLastDivBase = s_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(s_ulBase10); } n.AssertValid(); return n; }
// Round - Round the numeric to a specific digit /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> public static SqlDecimal Round(SqlDecimal n, int position) { n.AssertValid(); return Round(n, position, false); }
// Floor - next largest integer smaller or equal to the numeric /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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 /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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)); }
// 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 comparison: 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; } }
// Builtin functions // Abs - absolute value /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> public static SqlDecimal Abs(SqlDecimal n) { n.AssertValid(); if (n.IsNull) return SqlDecimal.Null; n.SetPositive(); n.AssertValid(); return n; }
// Convert to a specific precision and scale /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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; }
//----------------------------------------------------------- //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 reduced to 2, but since x_cNumeDivScaleMin // is set to 6 currently, resulting scale will be 6. // OverflowException is thrown 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. // /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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, s_cNumeDivScaleMin); ResInteger = x.m_bPrec - x.m_bScale + y.m_bScale; MinScale = Math.Min(ResScale, s_cNumeDivScaleMin); ResInteger = Math.Min(ResInteger, s_NUMERIC_MAX_PRECISION); ResPrec = ResInteger + ResScale; if (ResPrec > s_NUMERIC_MAX_PRECISION) ResPrec = s_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[s_cNumeMax + 1]; uint[] rgulQ = new uint[s_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; }
// 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 // precision = 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. // /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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 multiplier 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 precision = s1 + s2 + (p1 - s1) + (p2 - s2) + 1 ResPrec = ResScale + ResInteger; // Downward adjust res prec,scale if either larger than NUMERIC_MAX_PRECISION if (ResPrec > s_NUMERIC_MAX_PRECISION) ResPrec = s_NUMERIC_MAX_PRECISION; if (ResScale > s_NUMERIC_MAX_PRECISION) ResScale = s_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, s_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 = s_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 Debug.Assert(dwlAccum < s_ulInt32Base * 2); } Debug.Assert(dwlAccum < s_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) { Debug.Assert(s_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 = s_rgulShiftBase[8]; lScaleAdjust += 9; } else { ulShiftBase = s_rgulShiftBase[-lScaleAdjust - 1]; lScaleAdjust = 0; } MpDiv1(rgulRes, ref culRes, ulShiftBase, out ulRem); } while (lScaleAdjust != 0); // Still do not fit? if (culRes > s_cNumeMax) throw new OverflowException(SQLResource.ArithOverflowMessage); for (idRes = culRes; idRes < s_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 > s_cNumeMax) // Do not fit now, so will not fit after adjustment 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 < s_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 > s_cNumeMax) throw new OverflowException(SQLResource.ArithOverflowMessage); for (idRes = culRes; idRes < s_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; } }
// Binary operators // Arithmetic operators /// <devdoc> /// <para>[To be supplied.]</para> /// </devdoc> 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 precision = 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); Debug.Assert(ResInteger <= MaxPrecision); // Calculate the scale of the result. ResScale = Math.Max(MyScale, OpScale); Debug.Assert(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 integer part as ResInteger, not ResInteger + 1. Debug.Assert(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; // CONSIDER: Call AddUlong when possible // 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) { Debug.Assert(dwlAccum < s_ulInt32Base); //Either overflowed if (iulData == s_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 = s_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 += s_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() > s_NUMERIC_MAX_PRECISION) throw new OverflowException(SQLResource.ArithOverflowMessage); if (ret.FZero()) ret.SetPositive(); ret.AssertValid(); return ret; }
// Convert to a specific precision and scale 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 - ret._bPrec;//Adjustment to precision int lScaleAdjust = scale - ret._bScale;//Adjustment to scale //Adjust scale ret.AdjustScale(lScaleAdjust, true); //The number of bytes storage required by the new precision byte cbWithNewPrec = CLenFromPrec((byte)precision); if (cbWithNewPrec < ret._bLen) { //if current actual length greater than length corresponding to bNewPrec //there must be truncating throw new SqlTruncateException(); } else if (cbWithNewPrec == ret._bLen) { //if the two lengths equal, need to check the actual precision if (precision < ret.CalculatePrecision()) throw new SqlTruncateException(); } //Adjust precision ret._bPrec = (byte)precision; ret.AssertValid(); return ret; }