2 // System.Data.SqlTypes.SqlDecimal
5 // Tim Coleman <tim@timcoleman.com>
6 // Ville Palo <vi64pa@koti.soon.fi>
8 // (C) Copyright 2002 Tim Coleman
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using Mono.Data.Tds.Protocol;
38 using System.Xml.Schema;
39 using System.Globalization;
40 using System.Xml.Serialization;
42 namespace System.Data.SqlTypes
45 [SerializableAttribute]
46 [XmlSchemaProvider ("GetXsdType")]
48 public struct SqlDecimal : INullable, IComparable
62 // borrowed from System.Decimal
63 const int SCALE_SHIFT = 16;
64 const int SIGN_SHIFT = 31;
65 const int RESERVED_SS32_BITS = 0x7F00FFFF;
66 const ulong LIT_GUINT64_HIGHBIT = 0x8000000000000000;
67 const ulong LIT_GUINT32_HIGHBIT = 0x80000000;
68 const byte DECIMAL_MAX_INTFACTORS = 9;
69 static uint [] constantsDecadeInt32Factors = new uint [10] {
70 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u,
71 10000000u, 100000000u, 1000000000u};
73 public static readonly byte MaxPrecision = 38;
74 public static readonly byte MaxScale = 38;
76 // This should be 99999999999999999999999999999999999999
77 public static readonly SqlDecimal MaxValue = new SqlDecimal (MaxPrecision,
84 // This should be -99999999999999999999999999999999999999
85 public static readonly SqlDecimal MinValue = new SqlDecimal (MaxPrecision,
92 public static readonly SqlDecimal Null;
98 public SqlDecimal (decimal value)
100 int[] binData = Decimal.GetBits (value);
102 this.precision = MaxPrecision; // this value seems unclear
104 this.scale = (byte)(((uint)binData [3]) >> SCALE_SHIFT);
106 if (this.scale > MaxScale || ((uint)binData [3] & RESERVED_SS32_BITS) != 0)
107 throw new ArgumentException(Locale.GetText ("Invalid scale"));
109 this.value = new int[4];
110 this.value[0] = binData[0];
111 this.value[1] = binData[1];
112 this.value[2] = binData[2];
115 positive = (value >= 0);
117 precision = GetPrecision (value);
120 public SqlDecimal (double dVal) : this ((decimal) dVal)
123 int digits = 17 - precision;
125 n = AdjustScale (this, digits, false);
127 n = Round (this, 17);
128 this.notNull = n.notNull;
129 this.positive = n.positive;
130 this.precision = n.precision;
131 this.scale = n.scale;
132 this.value = n.value;
135 public SqlDecimal (int value) : this ((decimal) value)
139 public SqlDecimal (long value) : this ((decimal) value)
143 public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int[] bits) : this (bPrecision, bScale, fPositive, bits[0], bits[1], bits[2], bits[3])
147 public SqlDecimal (byte bPrecision, byte bScale, bool fPositive, int data1, int data2, int data3, int data4)
149 this.precision = bPrecision;
151 this.positive = fPositive;
152 this.value = new int[4];
153 this.value[0] = data1;
154 this.value[1] = data2;
155 this.value[2] = data3;
156 this.value[3] = data4;
159 if (precision < scale)
160 throw new SqlTypeException (Locale.GetText ("Invalid presicion/scale combination."));
163 throw new SqlTypeException (Locale.GetText ("Invalid precision/scale combination."));
165 if (this.ToDouble () > (Math.Pow (10, 38) - 1) ||
166 this.ToDouble () < -(Math.Pow (10, 38)))
167 throw new OverflowException ("Can't convert to SqlDecimal, Out of range ");
174 public byte[] BinData {
176 byte [] b = new byte [value.Length * 4];
179 for (int i = 0; i < value.Length; i++) {
181 b [j++] = (byte)(0xff & value [i]);
182 b [j++] = (byte)(0xff & value [i] >> 8);
183 b [j++] = (byte)(0xff & value [i] >> 16);
184 b [j++] = (byte)(0xff & value [i] >> 24);
194 throw new SqlNullValueException ();
195 // Data should always return clone, not to be modified
196 int [] ret = new int [4];
206 get { return !notNull; }
209 public bool IsPositive {
210 get { return positive; }
213 public byte Precision {
214 get { return precision; }
218 get { return scale; }
221 public decimal Value {
224 throw new SqlNullValueException ();
226 if (this.value[3] > 0)
227 throw new OverflowException ();
229 return new decimal (value[0], value[1], value[2], !positive, scale);
237 public static SqlDecimal Abs (SqlDecimal n)
241 return new SqlDecimal (n.Precision, n.Scale, true, n.Data);
244 public static SqlDecimal Add (SqlDecimal x, SqlDecimal y)
249 public static SqlDecimal AdjustScale (SqlDecimal n, int digits, bool fRound)
251 byte prec = n.Precision;
253 throw new SqlNullValueException ();
258 else if (digits > 0) {
259 prec = (byte)(prec + digits);
260 scale = (byte) (n.scale + digits);
261 // use Math.Pow once the Ctr (double) is fixed to handle
262 // values greater than Decimal.MaxValue
263 // the current code creates too many sqldecimal objects
264 //n = n * (new SqlDecimal ((double)Math.Pow (10, digits)));
265 for (int i = 0; i < digits; i++)
268 if (n.Scale < Math.Abs (digits))
269 throw new SqlTruncateException ();
272 n = Round (n, digits + n.scale);
274 n = Round (Truncate (n, digits + n.scale), digits + n.scale);
278 return new SqlDecimal (prec, scale, n.positive, n.Data);
281 public static SqlDecimal Ceiling (SqlDecimal n)
285 return AdjustScale (n, -(n.Scale), true);
288 public int CompareTo (object value)
292 if (!(value is SqlDecimal))
293 throw new ArgumentException (Locale.GetText ("Value is not a System.Data.SqlTypes.SqlDecimal"));
295 return CompareTo ((SqlDecimal) value);
301 int CompareTo (SqlDecimal value)
306 return this.Value.CompareTo (value.Value);
309 public static SqlDecimal ConvertToPrecScale (SqlDecimal n, int precision, int scale)
311 int prec = n.Precision;
313 n = AdjustScale (n, scale-n.scale, true);
314 if ((n.Scale >= sc) && (precision < n.Precision))
315 throw new SqlTruncateException ();
318 return new SqlDecimal ((byte)prec, n.scale, n.IsPositive, n.Data);
322 public static SqlDecimal Divide (SqlDecimal x, SqlDecimal y)
327 public override bool Equals (object value)
329 if (!(value is SqlDecimal))
331 else if (this.IsNull)
332 return ((SqlDecimal)value).IsNull;
333 else if (((SqlDecimal)value).IsNull)
336 return (bool) (this == (SqlDecimal)value);
339 public static SqlBoolean Equals (SqlDecimal x, SqlDecimal y)
344 public static SqlDecimal Floor (SqlDecimal n)
346 return AdjustScale (n, -(n.Scale), false);
349 internal static SqlDecimal FromTdsBigDecimal (TdsBigDecimal x)
354 return new SqlDecimal (x.Precision, x.Scale, !x.IsNegative, x.Data);
357 public override int GetHashCode ()
360 result = 91 * result + this.Data[0];
361 result = 91 * result + this.Data[1];
362 result = 91 * result + this.Data[2];
363 result = 91 * result + this.Data[3];
364 result = 91 * result + (int) this.Scale;
365 result = 91 * result + (int) this.Precision;
370 public static SqlBoolean GreaterThan (SqlDecimal x, SqlDecimal y)
375 public static SqlBoolean GreaterThanOrEqual (SqlDecimal x, SqlDecimal y)
380 public static SqlBoolean LessThan (SqlDecimal x, SqlDecimal y)
385 public static SqlBoolean LessThanOrEqual (SqlDecimal x, SqlDecimal y)
390 public static SqlDecimal Multiply (SqlDecimal x, SqlDecimal y)
395 public static SqlBoolean NotEquals (SqlDecimal x, SqlDecimal y)
400 public static SqlDecimal Parse (string s)
403 throw new ArgumentNullException (Locale.GetText ("string s"));
405 return new SqlDecimal (Decimal.Parse (s));
408 public static SqlDecimal Power (SqlDecimal n, double exp)
411 return SqlDecimal.Null;
413 return new SqlDecimal (Math.Pow (n.ToDouble (), exp));
416 public static SqlDecimal Round (SqlDecimal n, int position)
419 throw new SqlNullValueException ();
422 d = Decimal.Round (d, position);
423 return new SqlDecimal (d);
426 public static SqlInt32 Sign (SqlDecimal n)
429 return SqlInt32.Null;
430 return (SqlInt32) (n.IsPositive ? 1 : -1);
433 public static SqlDecimal Subtract (SqlDecimal x, SqlDecimal y)
438 private byte GetPrecision (decimal value)
440 string str = value.ToString ();
443 foreach (char c in str) {
445 if (c >= '0' && c <= '9')
452 public double ToDouble ()
454 // FIXME: This is wrong way to do this
455 double d = (uint)this.Data [0];
456 d += ((uint)this.Data [1]) * Math.Pow (2, 32);
457 d += ((uint)this.Data [2]) * Math.Pow (2, 64);
458 d += ((uint)this.Data [3]) * Math.Pow (2, 96);
459 d = d / Math.Pow (10, scale);
464 public SqlBoolean ToSqlBoolean ()
466 return ((SqlBoolean) this);
469 public SqlByte ToSqlByte ()
471 return ((SqlByte) this);
474 public SqlDouble ToSqlDouble ()
476 return ((SqlDouble) this);
479 public SqlInt16 ToSqlInt16 ()
481 return ((SqlInt16) this);
484 public SqlInt32 ToSqlInt32 ()
486 return ((SqlInt32) this);
489 public SqlInt64 ToSqlInt64 ()
491 return ((SqlInt64) this);
494 public SqlMoney ToSqlMoney ()
496 return ((SqlMoney) this);
499 public SqlSingle ToSqlSingle ()
501 return ((SqlSingle) this);
504 public SqlString ToSqlString ()
506 return ((SqlString) this);
509 public override string ToString ()
514 // convert int [4] --> ulong [2]
515 ulong lo = (uint)this.Data [0];
516 lo += (ulong)((ulong)this.Data [1] << 32);
517 ulong hi = (uint)this.Data [2];
518 hi += (ulong)((ulong)this.Data [3] << 32);
521 StringBuilder Result = new StringBuilder ();
522 for (int i = 0; lo != 0 || hi != 0; i++) {
523 Div128By32 (ref hi, ref lo, 10, ref rest);
524 Result.Insert (0, rest.ToString ());
527 while (Result.Length > this.Precision)
528 Result.Remove (Result.Length - 1, 1);
531 Result.Insert (Result.Length - this.Scale, ".");
534 Result.Insert (0, '-');
536 return Result.ToString ();
540 private static int Div128By32(ref ulong hi, ref ulong lo, uint divider)
543 return Div128By32 (ref hi, ref lo, divider, ref t);
547 private static int Div128By32(ref ulong hi, ref ulong lo, uint divider, ref uint rest)
553 a = (uint)(hi >> 32);
561 hi = b << 32 | (uint)c;
563 a |= (uint)(lo >> 32);
570 lo = b << 32 | (uint)c;
574 return (a > divider || (a == divider && (c & 1) == 1)) ? 1 : 0;
578 [MonoTODO("Find out what is the right way to set scale and precision")]
579 private static SqlDecimal DecimalDiv (SqlDecimal x, SqlDecimal y)
585 byte prec = 0; // precision
586 bool positive = ! (x.positive ^ y.positive);
588 prec = x.Precision >= y.Precision ? x.Precision : y.Precision;
589 DecimalDivSub (ref x, ref y, ref lo, ref hi, ref texp);
591 sc = x.Scale - y.Scale;
593 Rescale128 (ref lo, ref hi, ref sc, texp, 0, 38, 1);
597 Div128By32(ref hi, ref lo, 10, ref r);
604 while ((((double)hi) * Math.Pow(2,64) + lo) - Math.Pow (10, prec) > 0)
607 while ((prec + sc) > MaxScale) {
608 Div128By32(ref hi, ref lo, 10, ref r);
614 int resultLo = (int) lo;
615 int resultMi = (int) (lo >> 32);
616 int resultMi2 = (int) (hi);
617 int resultHi = (int) (hi >> 32);
619 return new SqlDecimal (prec, (byte) sc, positive, resultLo,
625 private static void Rescale128 (ref ulong clo, ref ulong chi,
626 ref int scale, int texp,
627 int minScale, int maxScale,
639 while (texp > 0 && sc <= maxScale) {
640 overhang = (uint)(chi >> 64);
641 while (texp > 0 && (((clo & 1) == 0) || overhang > 0)) {
643 roundBit = (int)(clo & 1);
644 RShift128 (ref clo, ref chi);
646 overhang = (uint)(chi >> 32);
649 if (texp > DECIMAL_MAX_INTFACTORS)
650 i = DECIMAL_MAX_INTFACTORS;
654 if (sc + i > maxScale)
664 factor = constantsDecadeInt32Factors [i] >> i;
665 Mult128By32 (ref clo, ref chi, factor, 0);
670 roundBit = (int)(clo & 1);
671 RShift128 (ref clo, ref chi);
675 while (sc > maxScale) {
676 i = scale - maxScale;
677 if (i > DECIMAL_MAX_INTFACTORS)
678 i = DECIMAL_MAX_INTFACTORS;
680 roundBit = Div128By32 (ref clo, ref chi,
681 constantsDecadeInt32Factors[i]);
684 while (sc < minScale) {
688 if (i > DECIMAL_MAX_INTFACTORS)
689 i = DECIMAL_MAX_INTFACTORS;
691 Mult128By32 (ref clo, ref chi,
692 constantsDecadeInt32Factors[i], roundBit);
696 Normalize128 (ref clo, ref chi, ref sc, roundFlag, roundBit);
700 private static void Normalize128(ref ulong clo, ref ulong chi, ref int scale, int roundFlag, int roundBit)
705 if ((roundFlag != 0) && (roundBit != 0))
706 RoundUp128 (ref clo, ref chi);
710 private static void RoundUp128(ref ulong lo, ref ulong hi)
717 private static void DecimalDivSub (ref SqlDecimal x, ref SqlDecimal y, ref ulong clo, ref ulong chi, ref int exp)
731 xhi = (ulong) ((ulong) x.Data [3] << 32) | (ulong) x.Data [2];
732 xmi = (ulong) ((ulong) x.Data [1] << 32) | (ulong) x.Data [0];
734 ylo = (uint) y.Data [0];
735 ymi = (uint) y.Data [1];
736 ymi2 = (uint) y.Data [2];
737 yhi = (uint) y.Data [3];
739 if (ylo == 0 && ymi == 0 && ymi2 == 0 && yhi == 0)
740 throw new DivideByZeroException ();
742 if (xmi == 0 && xhi == 0) {
747 // enlarge dividend to get maximal precision
748 for (ashift = 0; (xhi & LIT_GUINT64_HIGHBIT) == 0; ++ashift)
749 LShift128 (ref xmi, ref xhi);
751 // ensure that divisor is at least 2^95
752 for (bshift = 0; (yhi & LIT_GUINT32_HIGHBIT) == 0; ++bshift)
753 LShift128 (ref ylo, ref ymi, ref ymi2, ref yhi);
755 thi = ((ulong)yhi) << 32 | (ulong)ymi2;
756 tmi = ((ulong)ymi) << 32 | (ulong)ylo;
759 if (xhi > thi || (xhi == thi && xmi >=tmi)) {
760 Sub192(xlo, xmi, xhi, tlo, tmi, thi, ref xlo, ref xmi, ref xhi);
766 Div192By128To128 (xlo, xmi, xhi, ylo, ymi, ymi2, yhi, ref clo, ref chi);
768 exp = 128 + ashift - bshift;
771 RShift128 (ref clo, ref chi);
772 chi += LIT_GUINT64_HIGHBIT;
776 // try loss free right shift
777 while (exp > 0 && (clo & 1) == 0) {
778 RShift128 (ref clo, ref chi);
785 private static void RShift192(ref ulong lo, ref ulong mi, ref ulong hi)
789 lo |= LIT_GUINT64_HIGHBIT;
793 mi |= LIT_GUINT64_HIGHBIT;
800 private static void RShift128(ref ulong lo, ref ulong hi)
804 lo |= LIT_GUINT64_HIGHBIT;
809 private static void LShift128(ref ulong lo, ref ulong hi)
813 if ((lo & LIT_GUINT64_HIGHBIT) != 0)
820 private static void LShift128(ref uint lo, ref uint mi, ref uint mi2, ref uint hi)
823 if ((mi2 & LIT_GUINT32_HIGHBIT) != 0)
827 if ((mi & LIT_GUINT32_HIGHBIT) != 0)
831 if ((lo & LIT_GUINT32_HIGHBIT) != 0)
838 private static void Div192By128To128 (ulong xlo, ulong xmi, ulong xhi,
839 uint ylo, uint ymi, uint ymi2,
840 uint yhi, ref ulong clo, ref ulong chi)
842 ulong rlo, rmi, rhi; // remainders
849 h = Div192By128To32WithRest (ref rlo, ref rmi, ref rhi, ylo, ymi, ymi2, yhi);
852 rhi = (rhi << 32) | (rmi >> 32);
853 rmi = (rmi << 32) | (rlo >> 32);
856 chi = (((ulong)h) << 32) | Div192By128To32WithRest (
857 ref rlo, ref rmi, ref rhi, ylo, ymi, ymi2, yhi);
860 rhi = (rhi << 32) | (rmi >> 32);
861 rmi = (rmi << 32) | (rlo >> 32);
864 h = Div192By128To32WithRest (ref rlo, ref rmi, ref rhi,
865 ylo, ymi, ymi2, yhi);
867 // estimate lowest 32 bit (two last bits may be wrong)
872 c = (uint)(rhi / yhi);
875 clo = (((ulong)h) << 32) | c;
879 private static uint Div192By128To32WithRest(ref ulong xlo, ref ulong xmi,
880 ref ulong xhi, uint ylo,
881 uint ymi, uint ymi2, uint yhi)
883 ulong rlo, rmi, rhi; // remainder
892 if (rhi >= (((ulong)yhi << 32)))
895 c = (uint) (rhi / yhi);
897 Mult128By32To128 (ylo, ymi, ymi2, yhi, c, ref tlo, ref thi);
898 Sub192 (rlo, rmi, rhi, 0, tlo, thi, ref rlo, ref rmi, ref rhi);
900 while (((long)rhi) < 0) {
902 Add192 (rlo, rmi, rhi, 0, (((ulong)ymi) << 32) | ylo, yhi | ymi2, ref rlo, ref rmi, ref rhi);
913 private static void Mult192By32 (ref ulong clo, ref ulong cmi, ref ulong chi, ulong factor, int roundBit)
919 a = ((ulong)(uint)clo) * factor;
926 a += (clo >> 32) * factor;
929 clo = ((ulong)h1) << 32 | h0;
932 a += ((ulong)(uint)cmi) * factor;
936 a += (cmi >> 32) * factor;
939 cmi = ((ulong)h1) << 32 | h0;
941 a += ((ulong)(uint)chi) * factor;
945 a += (chi >> 32) * factor;
947 chi = ((ulong)h1) << 32 | h0;
952 private static void Mult128By32 (ref ulong clo, ref ulong chi, uint factor, int roundBit)
958 a = ((ulong)(uint)clo) * factor;
966 a += (clo >> 32) * factor;
969 clo = ((ulong)h1) << 32 | h0;
972 a += ((ulong)(uint)chi) * factor;
976 a += (chi >> 32) * factor;
979 chi = ((ulong)h1) << 32 | h0;
983 private static void Mult128By32To128(uint xlo, uint xmi, uint xmi2, uint xhi,
984 uint factor, ref ulong clo, ref ulong chi)
989 a = ((ulong)xlo) * factor;
993 a += ((ulong)xmi) * factor;
997 a += ((ulong)xmi2) * factor;
1001 a += ((ulong)xhi) * factor;
1003 clo = ((ulong)h1) << 32 | h0;
1008 private static void Add192 (ulong xlo, ulong xmi, ulong xhi,
1009 ulong ylo, ulong ymi, ulong yhi,
1010 ref ulong clo, ref ulong cmi, ref ulong chi)
1031 private static void Sub192 (ulong xlo, ulong xmi, ulong xhi,
1032 ulong ylo, ulong ymi, ulong yhi,
1033 ref ulong lo, ref ulong mi, ref ulong hi)
1058 public static SqlDecimal Truncate (SqlDecimal n, int position)
1060 int diff = n.scale - position;
1063 int [] data = n.Data;
1064 decimal d = new decimal (data [0], data [1], data [2], !n.positive, 0);
1066 for (int i = 0; i < diff; i++, x *= 10)
1068 data = Decimal.GetBits (d);
1070 return new SqlDecimal (n.precision, n.scale, n.positive, data);
1073 public static SqlDecimal operator + (SqlDecimal x, SqlDecimal y)
1075 if (x.IsNull || y.IsNull)
1076 return SqlDecimal.Null;
1077 //if one of them is negative, perform subtraction
1078 if (x.IsPositive && !y.IsPositive){
1079 y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
1082 if (!x.IsPositive && y.IsPositive){
1083 x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
1086 if (!x.IsPositive && !y.IsPositive){
1087 x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
1088 y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
1090 return new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
1092 // adjust the scale to the larger of the two beforehand
1093 if (x.scale > y.scale)
1094 y = SqlDecimal.AdjustScale (y, x.scale - y.scale, false);
1095 else if (y.scale > x.scale)
1096 x = SqlDecimal.AdjustScale (x, y.scale - x.scale, false);
1098 byte resultPrecision = (byte)(Math.Max (x.Scale, y.Scale) +
1099 Math.Max (x.Precision - x.Scale, y.Precision - y.Scale) + 1);
1101 if (resultPrecision > MaxPrecision)
1102 resultPrecision = MaxPrecision;
1104 int [] xData = x.Data;
1105 int [] yData = y.Data;
1106 int [] resultBits = new int[4];
1109 for (int i = 0; i < 4; i++){
1110 res = (ulong)((uint)xData [i]) + (ulong)((uint)yData [i]) + carry;
1111 resultBits [i] = (int) (res & (UInt32.MaxValue));
1116 throw new OverflowException ();
1118 return new SqlDecimal (resultPrecision, x.Scale, x.IsPositive, resultBits);
1121 public static SqlDecimal operator / (SqlDecimal x, SqlDecimal y)
1123 if (x.IsNull || y.IsNull)
1124 return SqlDecimal.Null;
1126 return DecimalDiv (x, y);
1129 public static SqlBoolean operator == (SqlDecimal x, SqlDecimal y)
1131 if (x.IsNull || y.IsNull)
1132 return SqlBoolean.Null;
1134 if (x.IsPositive != y.IsPositive)
1135 return SqlBoolean.False;
1137 if (x.Scale > y.Scale)
1138 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
1139 else if (y.Scale > x.Scale)
1140 x = SqlDecimal.AdjustScale(y, y.Scale - x.Scale, false);
1142 for (int i = 0; i < 4; i += 1)
1143 if (x.Data[i] != y.Data[i])
1144 return SqlBoolean.False;
1146 return SqlBoolean.True;
1149 public static SqlBoolean operator > (SqlDecimal x, SqlDecimal y)
1151 if (x.IsNull || y.IsNull)
1152 return SqlBoolean.Null;
1154 if (x.IsPositive != y.IsPositive)
1155 return new SqlBoolean (x.IsPositive);
1157 if (x.Scale > y.Scale)
1158 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, false);
1159 else if (y.Scale > x.Scale)
1160 x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, false);
1162 for (int i = 3; i >= 0; i--)
1164 if (x.Data[i] == 0 && y.Data[i] == 0)
1167 return new SqlBoolean (x.Data[i] > y.Data[i]);
1169 return new SqlBoolean (false);
1172 public static SqlBoolean operator >= (SqlDecimal x, SqlDecimal y)
1174 if (x.IsNull || y.IsNull)
1175 return SqlBoolean.Null;
1177 if (x.IsPositive != y.IsPositive)
1178 return new SqlBoolean (x.IsPositive);
1180 if (x.Scale > y.Scale)
1181 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
1182 else if (y.Scale > x.Scale)
1183 x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
1185 for (int i = 3; i >= 0; i -= 1)
1187 if (x.Data[i] == 0 && y.Data[i] == 0)
1190 return new SqlBoolean (x.Data[i] >= y.Data[i]);
1192 return new SqlBoolean (true);
1195 public static SqlBoolean operator != (SqlDecimal x, SqlDecimal y)
1197 if (x.IsNull || y.IsNull)
1198 return SqlBoolean.Null;
1200 if (x.IsPositive != y.IsPositive)
1201 return SqlBoolean.True;
1203 if (x.Scale > y.Scale)
1204 x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
1205 else if (y.Scale > x.Scale)
1206 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
1208 for (int i = 0; i < 4; i += 1)
1210 if (x.Data[i] != y.Data[i])
1211 return SqlBoolean.True;
1213 return SqlBoolean.False;
1216 public static SqlBoolean operator < (SqlDecimal x, SqlDecimal y)
1218 if (x.IsNull || y.IsNull)
1219 return SqlBoolean.Null;
1221 if (x.IsPositive != y.IsPositive)
1222 return new SqlBoolean (y.IsPositive);
1224 if (x.Scale > y.Scale)
1225 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
1226 else if (y.Scale > x.Scale)
1227 x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
1229 for (int i = 3; i >= 0; i -= 1) {
1230 if (x.Data[i] == 0 && y.Data[i] == 0)
1232 return new SqlBoolean (x.Data[i] < y.Data[i]);
1234 return new SqlBoolean (false);
1237 public static SqlBoolean operator <= (SqlDecimal x, SqlDecimal y)
1239 if (x.IsNull || y.IsNull)
1240 return SqlBoolean.Null;
1242 if (x.IsPositive != y.IsPositive)
1243 return new SqlBoolean (y.IsPositive);
1245 if (x.Scale > y.Scale)
1246 y = SqlDecimal.AdjustScale(y, x.Scale - y.Scale, true);
1247 else if (y.Scale > x.Scale)
1248 x = SqlDecimal.AdjustScale(x, y.Scale - x.Scale, true);
1250 for (int i = 3; i >= 0; i -= 1) {
1251 if (x.Data[i] == 0 && y.Data[i] == 0)
1254 return new SqlBoolean (x.Data[i] <= y.Data[i]);
1256 return new SqlBoolean (true);
1259 public static SqlDecimal operator * (SqlDecimal x, SqlDecimal y)
1261 if (x.IsNull || y.IsNull)
1262 return SqlDecimal.Null;
1264 // set the precision to the greater of the two
1265 byte resultPrecision = (byte)(x.Precision + y.Precision + 1);
1266 byte resultScale = (byte)(x.Scale + y.Scale);
1267 if (resultPrecision > MaxPrecision)
1268 resultPrecision = MaxPrecision;
1270 int[] xData = x.Data;
1271 int[] yData = y.Data;
1272 int[] resultBits = new int[4];
1277 for (int i=0; i<4; ++i) {
1279 for (int j=i; j<=i; ++j)
1280 res += ((ulong)(uint) xData[j]) * ((ulong)(uint) yData[i-j]);
1281 resultBits [i] = (int) ((res + carry) & UInt32.MaxValue);
1285 // if we have carry left, then throw an exception
1287 throw new OverflowException ();
1289 return new SqlDecimal (resultPrecision, resultScale, (x.IsPositive == y.IsPositive), resultBits);
1292 public static SqlDecimal operator - (SqlDecimal x, SqlDecimal y)
1294 if (x.IsNull || y.IsNull)
1295 return SqlDecimal.Null;
1297 if (x.IsPositive && !y.IsPositive){
1298 y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
1301 if (!x.IsPositive && y.IsPositive){
1302 x = new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
1304 return new SqlDecimal (x.Precision, x.Scale, false, x.Data);
1306 if (!x.IsPositive && !y.IsPositive){
1307 y = new SqlDecimal (y.Precision, y.Scale, !y.IsPositive, y.Data);
1308 x = new SqlDecimal(x.Precision, x.Scale, !x.IsPositive, x.Data);
1311 // adjust the scale to the larger of the two beforehand
1312 if (x.scale > y.scale)
1313 y = SqlDecimal.AdjustScale (y, x.scale - y.scale, false);
1314 else if (y.scale > x.scale)
1315 x = SqlDecimal.AdjustScale (x, y.scale - x.scale, false);
1317 //calculation of the new Precision for the result
1318 byte resultPrecision = (byte)(Math.Max (x.Scale, y.Scale) +
1319 Math.Max (x.Precision - x.Scale, y.Precision - y.Scale));
1333 int[] resultBits = new int[4];
1337 if ((uint)op2_Data [i] > (uint)op1_Data [i]) {
1338 carry = UInt32.MaxValue;
1339 op2_Data [i] = op2_Data [i] >> 1;
1342 res = (uint)carry; +(ulong)((uint)op1_Data [i]) - (ulong)((uint)op2_Data [i])
1345 for (int i = 0; i < 4; i += 1) {
1346 res = (ulong)((uint)op1_Data [i]) - (ulong)((uint)op2_Data [i]) + (ulong)carry;
1348 if ((uint)op2_Data [i] > (uint)op1_Data [i])
1350 resultBits [i] = (int)res;
1354 throw new OverflowException ();
1356 return new SqlDecimal (resultPrecision, x.Scale, (x>=y).Value, resultBits);
1359 public static SqlDecimal operator - (SqlDecimal x)
1361 return new SqlDecimal (x.Precision, x.Scale, !x.IsPositive, x.Data);
1364 public static explicit operator SqlDecimal (SqlBoolean x)
1369 return new SqlDecimal ((decimal)x.ByteValue);
1372 public static explicit operator Decimal (SqlDecimal x)
1377 public static explicit operator SqlDecimal (SqlDouble x)
1383 return new SqlDecimal ((double)x.Value);
1387 public static explicit operator SqlDecimal (SqlSingle x)
1393 return new SqlDecimal ((double)x.Value);
1397 public static explicit operator SqlDecimal (SqlString x)
1400 return Parse (x.Value);
1405 public static explicit operator SqlDecimal (double x)
1407 return new SqlDecimal (x);
1410 public static implicit operator SqlDecimal (long x)
1412 return new SqlDecimal (x);
1416 public static implicit operator SqlDecimal (decimal x)
1418 return new SqlDecimal (x);
1421 public static implicit operator SqlDecimal (SqlByte x)
1426 return new SqlDecimal ((decimal) x.Value);
1429 public static implicit operator SqlDecimal (SqlInt16 x)
1434 return new SqlDecimal ((decimal) x.Value);
1437 public static implicit operator SqlDecimal (SqlInt32 x)
1442 return new SqlDecimal ((decimal) x.Value);
1445 public static implicit operator SqlDecimal (SqlInt64 x)
1450 return new SqlDecimal ((decimal) x.Value);
1453 public static implicit operator SqlDecimal (SqlMoney x)
1458 return new SqlDecimal ((decimal) x.Value);
1462 public static XmlQualifiedName GetXsdType (XmlSchemaSet schemaSet)
1464 if (schemaSet != null && schemaSet.Count == 0) {
1465 XmlSchema xs = new XmlSchema ();
1466 XmlSchemaComplexType ct = new XmlSchemaComplexType ();
1467 ct.Name = "decimal";
1471 return new XmlQualifiedName ("decimal", "http://www.w3.org/2001/XMLSchema");
1474 XmlSchema IXmlSerializable.GetSchema ()
1479 void IXmlSerializable.ReadXml (XmlReader reader)
1486 switch (reader.ReadState) {
1487 case ReadState.EndOfFile:
1488 case ReadState.Error:
1489 case ReadState.Closed:
1493 // Skip XML declaration and prolog
1494 // or do I need to validate for the <SqlInt32> tag?
1495 reader.MoveToContent ();
1500 if (reader.NodeType == XmlNodeType.EndElement)
1503 if (reader.Value.Length > 0) {
1504 if (String.Compare ("Null", reader.Value) == 0) {
1505 // means a null reference/invalid value
1509 // FIXME: do we need to handle the FormatException?
1510 retval = new SqlDecimal (Decimal.Parse (reader.Value));
1512 // SqlDecimal.Data returns a clone'd array
1513 this.value = retval.Data;
1514 this.notNull = true;
1515 this.scale = retval.Scale;
1516 this.precision = retval.Precision;
1517 this.positive = retval.IsPositive;
1521 void IXmlSerializable.WriteXml (XmlWriter writer)
1523 writer.WriteString (this.Value.ToString ());