//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- using System; using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Basetypes { using BIM = System.Numerics.BigInteger; /// /// A representation of decimal values. /// public struct BigDec { // the internal representation [Rep] internal readonly BIM mantissa; [Rep] internal readonly int exponent; public BIM Mantissa { get { return mantissa; } } public int Exponent { get { return exponent; } } public static readonly BigDec ZERO = FromInt(0); private static readonly BIM ten = new BIM(10); //////////////////////////////////////////////////////////////////////////// // Constructors [Pure] public static BigDec FromInt(int v) { return new BigDec(v, 0); } [Pure] public static BigDec FromString(string v) { if (v == null) throw new FormatException(); BIM integral = BIM.Zero; BIM fraction = BIM.Zero; int exponent = 0; int len = v.Length; int i = v.IndexOf('e'); if (i >= 0) { if (i + 1 == v.Length) throw new FormatException(); exponent = Int32.Parse(v.Substring(i + 1, len - i - 1)); len = i; } int fractionLen = 0; i = v.IndexOf('.'); if (i >= 0) { if (i + 1 == v.Length) throw new FormatException(); fractionLen = len - i - 1; fraction = BIM.Parse(v.Substring(i + 1, fractionLen)); len = i; } integral = BIM.Parse(v.Substring(0, len)); if (!fraction.IsZero) { while (fractionLen > 0) { integral = integral * ten; exponent = exponent - 1; fractionLen = fractionLen - 1; } } return new BigDec(integral + fraction, exponent); } internal BigDec(BIM mantissa, int exponent) { if (mantissa.IsZero) { this.mantissa = mantissa; this.exponent = 0; } else { while (mantissa % ten == BIM.Zero) { mantissa = mantissa / ten; exponent = exponent + 1; } this.mantissa = mantissa; this.exponent = exponent; } } //////////////////////////////////////////////////////////////////////////// // Basic object operations [Pure] [Reads(ReadsAttribute.Reads.Nothing)] public override bool Equals(object obj) { if (obj == null) return false; if (!(obj is BigDec)) return false; return (this == (BigDec)obj); } [Pure] public override int GetHashCode() { return this.mantissa.GetHashCode() * 13 + this.exponent.GetHashCode(); } [Pure] public override string/*!*/ ToString() { Contract.Ensures(Contract.Result() != null); return String.Format("{0}e{1}", this.mantissa.ToString(), this.exponent.ToString()); } //////////////////////////////////////////////////////////////////////////// // Conversion operations [Pure] public BIM Floor(BIM? minimum, BIM? maximum) { BIM n = this.mantissa; if (this.exponent >= 0) { int e = this.exponent; while (e > 0 && (minimum == null || minimum <= n) && (maximum == null || n <= maximum)) { n = n * ten; e = e - 1; } } else { int e = -this.exponent; while (e > 0 && !n.IsZero) { n = n / ten; e = e - 1; } } if (minimum != null && n < minimum) return (BIM)minimum; else if (maximum != null && maximum < n) return (BIM)maximum; else return n; } [Pure] public String ToDecimalString(int maxDigits) { string s = this.mantissa.ToString(); int digits = (this.mantissa >= 0) ? s.Length : s.Length - 1; BIM max = BIM.Pow(10, maxDigits); BIM min = -max; if (this.exponent >= 0) { if (maxDigits < digits || maxDigits - digits < this.exponent) { return String.Format("{0}.0", (this.mantissa >= 0) ? max.ToString() : min.ToString()); } else { return String.Format("{0}{1}.0", s, new string('0', this.exponent)); } } else { int exp = -this.exponent; if (exp < digits) { int intDigits = digits - exp; if (maxDigits < intDigits) { return String.Format("{0}.0", (this.mantissa >= 0) ? max.ToString() : min.ToString()); } else { int fracDigits = Math.Min(maxDigits, digits - intDigits); return String.Format("{0}.{1}", s.Substring(0, intDigits), s.Substring(intDigits, fracDigits)); } } else { int fracDigits = Math.Min(maxDigits, digits); return String.Format("0.{0}{1}", new string('0', exp - fracDigits), s.Substring(0, fracDigits)); } } } //////////////////////////////////////////////////////////////////////////// // Basic arithmetic operations [Pure] public BigDec Abs { get { return new BigDec(BIM.Abs(this.mantissa), this.exponent); } } [Pure] public BigDec Negate { get { return new BigDec(BIM.Negate(this.mantissa), this.exponent); } } [Pure] public static BigDec operator -(BigDec x) { return x.Negate; } [Pure] public static BigDec operator +(BigDec x, BigDec y) { BIM m1 = x.mantissa; int e1 = x.exponent; BIM m2 = y.mantissa; int e2 = y.exponent; if (e2 < e1) { m1 = y.mantissa; e1 = y.exponent; m2 = x.mantissa; e2 = x.exponent; } while (e2 > e1) { m2 = m2 * ten; e2 = e2 - 1; } return new BigDec(m1 + m2, e1); } [Pure] public static BigDec operator -(BigDec x, BigDec y) { return x + y.Negate; } [Pure] public static BigDec operator *(BigDec x, BigDec y) { return new BigDec(x.mantissa * y.mantissa, x.exponent + y.exponent); } //////////////////////////////////////////////////////////////////////////// // Some basic comparison operations public bool IsPositive { get { return (this.mantissa > BIM.Zero); } } public bool IsNegative { get { return (this.mantissa < BIM.Zero); } } public bool IsZero { get { return this.mantissa.IsZero; } } [Pure] public int CompareTo(BigDec that) { if (this.mantissa == that.mantissa && this.exponent == that.exponent) { return 0; } else { BigDec d = this - that; return d.IsNegative ? -1 : 1; } } [Pure] public static bool operator ==(BigDec x, BigDec y) { return x.CompareTo(y) == 0; } [Pure] public static bool operator !=(BigDec x, BigDec y) { return x.CompareTo(y) != 0; } [Pure] public static bool operator <(BigDec x, BigDec y) { return x.CompareTo(y) < 0; } [Pure] public static bool operator >(BigDec x, BigDec y) { return x.CompareTo(y) > 0; } [Pure] public static bool operator <=(BigDec x, BigDec y) { return x.CompareTo(y) <= 0; } [Pure] public static bool operator >=(BigDec x, BigDec y) { return x.CompareTo(y) >= 0; } } }