// 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;
}