// 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; }
// 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; }
public static SqlDecimal operator +(SqlDecimal x, SqlDecimal y) { int num; ulong num2; byte num11; if (x.IsNull || y.IsNull) { return Null; } bool fPositive = true; bool isPositive = x.IsPositive; bool flag2 = y.IsPositive; int bScale = x.m_bScale; int num7 = y.m_bScale; int num9 = Math.Max((int) (x.m_bPrec - bScale), (int) (y.m_bPrec - num7)); int num3 = Math.Max(bScale, num7); int num6 = (num9 + num3) + 1; num6 = Math.Min(MaxPrecision, num6); if ((num6 - num9) < num3) { num3 = num6 - num9; } if (bScale != num3) { x.AdjustScale(num3 - bScale, true); } if (num7 != num3) { y.AdjustScale(num3 - num7, true); } if (!isPositive) { isPositive = !isPositive; flag2 = !flag2; fPositive = !fPositive; } int bLen = x.m_bLen; int num4 = y.m_bLen; uint[] rglData = 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 }; if (flag2) { num2 = 0L; num = 0; while ((num < bLen) || (num < num4)) { if (num < bLen) { num2 += rglData[num]; } if (num < num4) { num2 += numArray4[num]; } rglData[num] = (uint) num2; num2 = num2 >> 0x20; num++; } if (num2 != 0L) { if (num == x_cNumeMax) { throw new OverflowException(SQLResource.ArithOverflowMessage); } rglData[num] = (uint) num2; num++; } num11 = (byte) num; } else { int num10 = 0; if (x.LAbsCmp(y) < 0) { fPositive = !fPositive; uint[] numArray5 = numArray4; numArray4 = rglData; rglData = numArray5; bLen = num4; num4 = x.m_bLen; } num2 = x_ulInt32Base; for (num = 0; (num < bLen) || (num < num4); num++) { if (num < bLen) { num2 += rglData[num]; } if (num < num4) { num2 -= numArray4[num]; } rglData[num] = (uint) num2; if (rglData[num] != 0) { num10 = num; } num2 = num2 >> 0x20; num2 += x_ulInt32BaseForMod; } num11 = (byte) (num10 + 1); } SqlDecimal num12 = new SqlDecimal(rglData, num11, (byte) num6, (byte) num3, fPositive); if (num12.FGt10_38() || (num12.CalculatePrecision() > NUMERIC_MAX_PRECISION)) { throw new OverflowException(SQLResource.ArithOverflowMessage); } if (num12.FZero()) { num12.SetPositive(); } return num12; }