//
// System.Decimal.cs
//
// Represents a floating-point decimal data type with up to 29
// significant digits, suitable for financial and commercial calculations.
//
// Author:
// Martin Weindel (martin.weindel@t-online.de)
//
// (C) 2001 Martin Weindel
//
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Globalization;
using System.Text;
using System.Runtime.CompilerServices;
#if MSTEST
using System.Runtime.InteropServices;
#endif
#if NET_2_0
using System.Runtime.ConstrainedExecution;
#endif
namespace System
{
/// <summary>
/// Represents a floating-point decimal data type with up to 29 significant
/// digits, suitable for financial and commercial calculations
/// </summary>
[Serializable]
public struct Decimal: IFormattable, IConvertible, IComparable
#if NET_2_0
, IComparable<Decimal>, IEquatable <Decimal>
#endif
{
#if BOOTSTRAP_WITH_OLDLIB
// LAMESPEC: the attributes aren't mentioned, but show up in CorCompare
// Unfortunately, corcompare starts throwing security exceptions when
// these attributes are present...
[DecimalConstantAttribute(0, 1, unchecked((uint)-1), unchecked((uint)-1), unchecked((uint)-1))]
public static readonly Decimal MinValue = new Decimal(-1, -1, -1, true, 0);
[DecimalConstantAttribute(0, 0, unchecked((uint)-1), unchecked((uint)-1), unchecked((uint)-1))]
public static readonly Decimal MaxValue = new Decimal(-1, -1, -1, false, 0);
[DecimalConstantAttribute(0, 1, 0, 0, 1)]
public static readonly Decimal MinusOne = new Decimal(1, 0, 0, true, 0);
[DecimalConstantAttribute(0, 0, 0, 0, 1)]
public static readonly Decimal One = new Decimal(1, 0, 0, false, 0);
[DecimalConstantAttribute(0, 0, 0, 0, 0)]
public static readonly Decimal Zero = new Decimal(0, 0, 0, false, 0);
#else
public const decimal MinValue = -79228162514264337593543950335m;
public const decimal MaxValue = 79228162514264337593543950335m;
public const decimal MinusOne = -1;
public const decimal One = 1;
public const decimal Zero = 0;
#endif
private static readonly Decimal MaxValueDiv10 = MaxValue / 10;
// maximal decimal value as double
private static readonly double dDecMaxValue = 7.922816251426433759354395033e28;
// epsilon decimal value as double
private static readonly double dDecEpsilon = 0.5e-28; // == 0.5 * 1 / 10^28
// some constants
private const int DECIMAL_DIVIDE_BY_ZERO = 5;
private const uint MAX_SCALE = 28;
private const int iMAX_SCALE = 28;
private const uint SIGN_FLAG = 0x80000000;
private const uint SCALE_MASK = 0x00FF0000;
private const int SCALE_SHIFT = 16;
private const uint RESERVED_SS32_BITS = 0x7F00FFFF;
// internal representation of decimal
private uint flags;
private uint hi;
private uint lo;
private uint mid;
public Decimal(int lo, int mid, int hi, bool isNegative, byte scale)
{
unchecked
{
this.lo = (uint) lo;
this.mid = (uint) mid;
this.hi = (uint) hi;
if (scale > MAX_SCALE)
{
throw new ArgumentOutOfRangeException (Locale.GetText ("scale must be between 0 and 28"));
}
flags = scale;
flags <<= SCALE_SHIFT;
if (isNegative) flags |= SIGN_FLAG;
}
}
public Decimal(int val)
{
unchecked
{
hi = mid = 0;
if (val < 0)
{
flags = SIGN_FLAG;
lo = ((uint)~val) + 1;
}
else
{
flags = 0;
lo = (uint) val;
}
}
}
[CLSCompliant(false)]
public Decimal(uint val)
{
lo = val;
flags = hi = mid = 0;
}
public Decimal(long val)
{
unchecked
{
hi = 0;
if (val < 0)
{
flags = SIGN_FLAG;
ulong u = ((ulong)~val) + 1;
lo = (uint)u;
mid = (uint)(u >> 32);
}
else
{
flags = 0;
ulong u = (ulong)val;
lo = (uint)u;
mid = (uint)(u >> 32);
}
}
}
[CLSCompliant(false)]
public Decimal(ulong uval)
{
unchecked
{
flags = hi = 0;
lo = (uint)uval;
mid = (uint)(uval >> 32);
}
}
public Decimal (float val)
{
if (val > (float)Decimal.MaxValue || val < (float)Decimal.MinValue) {
throw new OverflowException (Locale.GetText (
"Value is greater than Decimal.MaxValue or less than Decimal.MinValue"));
}
// we must respect the precision (double2decimal doesn't)
Decimal d = Decimal.Parse (val.ToString (CultureInfo.InvariantCulture),
NumberStyles.Float, CultureInfo.InvariantCulture);
flags = d.flags;
hi = d.hi;
lo = d.lo;
mid = d.mid;
}
public Decimal (double val)
{
if (val > (double)Decimal.MaxValue || val < (double)Decimal.MinValue) {
throw new OverflowException (Locale.GetText (
"Value is greater than Decimal.MaxValue or less than Decimal.MinValue"));
}
// we must respect the precision (double2decimal doesn't)
Decimal d = Decimal.Parse (val.ToString (CultureInfo.InvariantCulture),
NumberStyles.Float, CultureInfo.InvariantCulture);
flags = d.flags;
hi = d.hi;
lo = d.lo;
mid = d.mid;
}
public Decimal(int[] bits)
{
if (bits == null)
{
throw new ArgumentNullException(Locale.GetText ("Bits is a null reference"));
}
if (bits.GetLength(0) != 4)
{
throw new ArgumentException(Locale.GetText ("bits does not contain four values"));
}
unchecked {
lo = (uint) bits[0];
mid = (uint) bits[1];
hi = (uint) bits[2];
flags = (uint) bits[3];
byte scale = (byte)(flags >> SCALE_SHIFT);
if (scale > MAX_SCALE || (flags & RESERVED_SS32_BITS) != 0)
{
throw new ArgumentException(Locale.GetText ("Invalid bits[3]"));
}
}
}
public static decimal FromOACurrency(long cy)
{
return (decimal)cy / (decimal)10000;
}
public static int[] GetBits(Decimal d)
{
unchecked
{
return new int[] { (int)d.lo, (int)d.mid, (int)d.hi,
(int)d.flags };
}
}
public static Decimal Negate(Decimal d)
{
d.flags ^= SIGN_FLAG;
return d;
}
public static Decimal Add(Decimal d1, Decimal d2)
{
if (decimalIncr(ref d1, ref d2) == 0)
return d1;
else
throw new OverflowException(Locale.GetText ("Overflow on adding decimal number"));
}
public static Decimal Subtract(Decimal d1, Decimal d2)
{
d2.flags ^= SIGN_FLAG;
int result = decimalIncr(ref d1, ref d2);
if (result == 0)
return d1;
else
throw new OverflowException(Locale.GetText ("Overflow on subtracting decimal numbers ("+result+")"));
}
public override int GetHashCode ()
{
return (int) (flags ^ hi ^ lo ^ mid);
}
public static Decimal operator +(Decimal d1, Decimal d2)
{
return Add(d1, d2);
}
public static Decimal operator --(Decimal d)
{
return Add(d, MinusOne);
}
public static Decimal operator ++(Decimal d)
{
return Add(d, One);
}
public static Decimal operator -(Decimal d1, Decimal d2)
{
return Subtract(d1, d2);
}
public static Decimal operator -(Decimal d)
{
return Negate(d);
}
public static Decimal operator +(Decimal d)
{
return d;
}
public static Decimal operator *(Decimal d1, Decimal d2)
{
return Multiply(d1, d2);
}
public static Decimal operator /(Decimal d1, Decimal d2)
{
return Divide(d1, d2);
}
public static Decimal operator %(Decimal d1, Decimal d2)
{
return Remainder(d1, d2);
}
private static ulong u64 (Decimal value)
{
ulong result;
decimalFloorAndTrunc (ref value, 0);
if (decimal2UInt64 (ref value, out result) != 0) {
throw new System.OverflowException ();
}
return result;
}
private static long s64 (Decimal value)
{
long result;
decimalFloorAndTrunc (ref value, 0);
if (decimal2Int64 (ref value, out result) != 0) {
throw new System.OverflowException ();
}
return result;
}
public static explicit operator byte (Decimal val)
{
ulong result = u64 (val);
return checked ((byte) result);
}
[CLSCompliant (false)]
public static explicit operator sbyte (Decimal val)
{
long result = s64 (val);
return checked ((sbyte) result);
}
public static explicit operator char (Decimal val)
{
ulong result = u64 (val);
return checked ((char) result);
}
public static explicit operator short (Decimal val)
{
long result = s64 (val);
return checked ((short) result);
}
[CLSCompliant (false)]
public static explicit operator ushort (Decimal val)
{
ulong result = u64 (val);
return checked ((ushort) result);
}
public static explicit operator int (Decimal val)
{
long result = s64 (val);
return checked ((int) result);
}
[CLSCompliant(false)]
public static explicit operator uint (Decimal val)
{
ulong result = u64 (val);
return checked ((uint) result);
}
public static explicit operator long (Decimal val)
{
return s64 (val);
}
[CLSCompliant(false)]
public static explicit operator ulong (Decimal val)
{
return u64 (val);
}
public static implicit operator Decimal(byte val)
{
return new Decimal(val);
}
[CLSCompliant(false)]
public static implicit operator Decimal(sbyte val)
{
return new Decimal(val);
}
public static implicit operator Decimal(short val)
{
return new Decimal(val);
}
[CLSCompliant(false)]
public static implicit operator Decimal(ushort val)
{
return new Decimal(val);
}
public static implicit operator Decimal(char val)
{
return new Decimal(val);
}
public