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; }
// 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; }
// 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; } }
public static SqlDecimal operator *(SqlDecimal x, SqlDecimal y) { SqlDecimal num11; if (x.IsNull || y.IsNull) { return Null; } int bLen = y.m_bLen; int num10 = x.m_bScale + y.m_bScale; int num3 = num10; int num13 = ((x.m_bPrec - x.m_bScale) + (y.m_bPrec - y.m_bScale)) + 1; int num6 = num3 + num13; if (num6 > NUMERIC_MAX_PRECISION) { num6 = NUMERIC_MAX_PRECISION; } if (num3 > NUMERIC_MAX_PRECISION) { num3 = NUMERIC_MAX_PRECISION; } num3 = Math.Max(Math.Min(num6 - num13, num3), Math.Min(num10, x_cNumeDivScaleMin)); int digits = num3 - num10; bool fPositive = x.IsPositive == y.IsPositive; uint[] numArray5 = new uint[] { x.m_data1, x.m_data2, x.m_data3, x.m_data4 }; uint[] numArray4 = new uint[] { y.m_data1, y.m_data2, y.m_data3, y.m_data4 }; uint[] rgulU = new uint[9]; int index = 0; for (int i = 0; i < x.m_bLen; i++) { uint num16 = numArray5[i]; ulong num2 = 0L; index = i; for (int j = 0; j < bLen; j++) { ulong num7 = num2 + rgulU[index]; ulong num15 = numArray4[j]; num2 = num16 * num15; num2 += num7; if (num2 < num7) { num7 = x_ulInt32Base; } else { num7 = 0L; } rgulU[index++] = (uint) num2; num2 = (num2 >> 0x20) + num7; } if (num2 != 0L) { rgulU[index++] = (uint) num2; } } while ((rgulU[index] == 0) && (index > 0)) { index--; } int ciulU = index + 1; if (digits != 0) { if (digits < 0) { uint num12; uint num14; do { if (digits <= -9) { num12 = x_rgulShiftBase[8]; digits += 9; } else { num12 = x_rgulShiftBase[-digits - 1]; digits = 0; } MpDiv1(rgulU, ref ciulU, num12, out num14); } while (digits != 0); if (ciulU > x_cNumeMax) { throw new OverflowException(SQLResource.ArithOverflowMessage); } for (index = ciulU; index < x_cNumeMax; index++) { rgulU[index] = 0; } num11 = new SqlDecimal(rgulU, (byte) ciulU, (byte) num6, (byte) num3, fPositive); if (num11.FGt10_38()) { throw new OverflowException(SQLResource.ArithOverflowMessage); } if (num14 >= (num12 / 2)) { num11.AddULong(1); } if (num11.FZero()) { num11.SetPositive(); } return num11; } if (ciulU > x_cNumeMax) { throw new OverflowException(SQLResource.ArithOverflowMessage); } for (index = ciulU; index < x_cNumeMax; index++) { rgulU[index] = 0; } num11 = new SqlDecimal(rgulU, (byte) ciulU, (byte) num6, (byte) num10, fPositive); if (num11.FZero()) { num11.SetPositive(); } num11.AdjustScale(digits, true); return num11; } if (ciulU > x_cNumeMax) { throw new OverflowException(SQLResource.ArithOverflowMessage); } for (index = ciulU; index < x_cNumeMax; index++) { rgulU[index] = 0; } num11 = new SqlDecimal(rgulU, (byte) ciulU, (byte) num6, (byte) num3, fPositive); if (num11.FGt10_38()) { throw new OverflowException(SQLResource.ArithOverflowMessage); } if (num11.FZero()) { num11.SetPositive(); } return num11; }
private static SqlDecimal Round(SqlDecimal n, int lPosition, bool fTruncate) { if (n.IsNull) { return Null; } if (lPosition >= 0) { lPosition = Math.Min(NUMERIC_MAX_PRECISION, lPosition); if (lPosition >= n.m_bScale) { return n; } } else { lPosition = Math.Max(-NUMERIC_MAX_PRECISION, lPosition); if (lPosition < (n.m_bScale - n.m_bPrec)) { n.SetToZero(); return n; } } uint num2 = 0; int num = Math.Abs((int) (lPosition - n.m_bScale)); uint num3 = 1; while (num > 0) { if (num >= 9) { num2 = n.DivByULong(x_rgulShiftBase[8]); num3 = x_rgulShiftBase[8]; num -= 9; } else { num2 = n.DivByULong(x_rgulShiftBase[num - 1]); num3 = x_rgulShiftBase[num - 1]; num = 0; } } if (num3 > 1) { num2 /= num3 / 10; } if (n.FZero() && (fTruncate || (num2 < 5))) { n.SetPositive(); return n; } if ((num2 >= 5) && !fTruncate) { n.AddULong(1); } num = Math.Abs((int) (lPosition - n.m_bScale)); while (num-- > 0) { n.MultByULong(x_ulBase10); } return n; }
public static SqlDecimal Floor(SqlDecimal n) { if (n.IsNull) { return Null; } if (n.m_bScale != 0) { bool flag; n.MakeInteger(out flag); if (flag && !n.IsPositive) { n.AddULong(1); } if (n.FZero()) { n.SetPositive(); } } return n; }