// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; namespace System { // Implements the Decimal data type. The Decimal data type can // represent values ranging from -79,228,162,514,264,337,593,543,950,335 to // 79,228,162,514,264,337,593,543,950,335 with 28 significant digits. The // Decimal data type is ideally suited to financial calculations that // require a large number of significant digits and no round-off errors. // // The finite set of values of type Decimal are of the form m // / 10e, where m is an integer such that // -296 <; m <; 296, and e is an integer // between 0 and 28 inclusive. // // Contrary to the float and double data types, decimal // fractional numbers such as 0.1 can be represented exactly in the // Decimal representation. In the float and double // representations, such numbers are often infinite fractions, making those // representations more prone to round-off errors. // // The Decimal class implements widening conversions from the // ubyte, char, short, int, and long types // to Decimal. These widening conversions never lose any information // and never throw exceptions. The Decimal class also implements // narrowing conversions from Decimal to ubyte, char, // short, int, and long. These narrowing conversions round // the Decimal value towards zero to the nearest integer, and then // converts that integer to the destination type. An OverflowException // is thrown if the result is not within the range of the destination type. // // The Decimal class provides a widening conversion from // Currency to Decimal. This widening conversion never loses any // information and never throws exceptions. The Currency class provides // a narrowing conversion from Decimal to Currency. This // narrowing conversion rounds the Decimal to four decimals and then // converts that number to a Currency. An OverflowException // is thrown if the result is not within the range of the Currency type. // // The Decimal class provides narrowing conversions to and from the // float and double types. A conversion from Decimal to // float or double may lose precision, but will not lose // information about the overall magnitude of the numeric value, and will never // throw an exception. A conversion from float or double to // Decimal throws an OverflowException if the value is not within // the range of the Decimal type. [StructLayout(LayoutKind.Sequential)] [Serializable] [NonVersionable] // This only applies to field layout [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly partial struct Decimal : ISpanFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal>, ISerializable, IDeserializationCallback, IFloatingPoint<decimal>, IMinMaxValue<decimal>, IUtf8SpanFormattable { // Sign mask for the flags field. A value of zero in this bit indicates a // positive Decimal value, and a value of one in this bit indicates a // negative Decimal value. // // Look at OleAut's DECIMAL_NEG constant to check for negative values // in native code. private const int SignMask = unchecked((int)0x80000000); // Scale mask for the flags field. This byte in the flags field contains // the power of 10 to divide the Decimal value by. The scale byte must // contain a value between 0 and 28 inclusive. private const int ScaleMask = 0x00FF0000; // Number of bits scale is shifted by. private const int ScaleShift = 16; // Constant representing the Decimal value 0. public const decimal Zero = 0m; // Constant representing the Decimal value 1. public const decimal One = 1m; // Constant representing the Decimal value -1. public const decimal MinusOne = -1m; // Constant representing the largest possible Decimal value. The value of // this constant is 79,228,162,514,264,337,593,543,950,335. public const decimal MaxValue = 79228162514264337593543950335m; // Constant representing the smallest possible Decimal value. The value of // this constant is -79,228,162,514,264,337,593,543,950,335. public const decimal MinValue = -79228162514264337593543950335m; /// <summary>Represents the additive identity (0).</summary> private const decimal AdditiveIdentity = 0m; /// <summary>Represents the multiplicative identity (1).</summary> private const decimal MultiplicativeIdentity = 1m; /// <summary>Represents the number negative one (-1).</summary> private const decimal NegativeOne = -1m; // The lo, mid, hi, and flags fields contain the representation of the // Decimal value. The lo, mid, and hi fields contain the 96-bit integer // part of the Decimal. Bits 0-15 (the lower word) of the flags field are // unused and must be zero; bits 16-23 contain must contain a value between // 0 and 28, indicating the power of 10 to divide the 96-bit integer part // by to produce the Decimal value; bits 24-30 are unused and must be zero; // and finally bit 31 indicates the sign of the Decimal value, 0 meaning // positive and 1 meaning negative. // // NOTE: Do not change the order and types of these fields. The layout has to // match Win32 DECIMAL type. private readonly int _flags; private readonly uint _hi32; private readonly ulong _lo64; // Constructs a Decimal from an integer value. // public Decimal(int value) { if (value >= 0) { _flags = 0; } else { _flags = SignMask; value = -value; } _lo64 = (uint)value; _hi32 = 0; } // Constructs a Decimal from an unsigned integer value. // [CLSCompliant(false)] public Decimal(uint value) { _flags = 0; _lo64 = value; _hi32 = 0; } // Constructs a Decimal from a long value. // public Decimal(long value) { if (value >= 0) { _flags = 0; } else { _flags = SignMask; value = -value; } _lo64 = (ulong)value; _hi32 = 0; } // Constructs a Decimal from an unsigned long value. // [CLSCompliant(false)] public Decimal(ulong value) { _flags = 0; _lo64 = value; _hi32 = 0; } // Constructs a Decimal from a float value. // public Decimal(float value) { DecCalc.VarDecFromR4(value, out AsMutable(ref this)); } // Constructs a Decimal from a double value. // public Decimal(double value) { DecCalc.VarDecFromR8(value, out AsMutable(ref this)); } private Decimal(SerializationInfo info, StreamingContext context) { ArgumentNullException.ThrowIfNull(info); _flags = info.GetInt32("flags"); _hi32 = (uint)info.GetInt32("hi"); _lo64 = (uint)info.GetInt32("lo") + ((ulong)info.GetInt32("mid") << 32); } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { ArgumentNullException.ThrowIfNull(info); // Serialize both the old and the new format info.AddValue("flags", _flags); info.AddValue("hi", (int)High); info.AddValue("lo", (int)Low); info.AddValue("mid", (int)Mid); } // // Decimal <==> Currency conversion. // // A Currency represents a positive or negative decimal value with 4 digits past the decimal point. The actual Int64 representation used by these methods // is the currency value multiplied by 10,000. For example, a currency value of $12.99 would be represented by the Int64 value 129,900. // public static decimal FromOACurrency(long cy) { ulong absoluteCy; // has to be ulong to accommodate the case where cy == long.MinValue. bool isNegative = false; if (cy < 0) { isNegative = true; absoluteCy = (ulong)(-cy); } else { absoluteCy = (ulong)cy; } // In most cases, FromOACurrency() produces a Decimal with Scale set to 4. Unless, that is, some of the trailing digits past the decimal point are zero, // in which case, for compatibility with .Net, we reduce the Scale by the number of zeros. While the result is still numerically equivalent, the scale does // affect the ToString() value. In particular, it prevents a converted currency value of $12.95 from printing uglily as "12.9500". int scale = 4; if (absoluteCy != 0) // For compatibility, a currency of 0 emits the Decimal "0.0000" (scale set to 4). { while (scale != 0 && ((absoluteCy % 10) == 0)) { scale--; absoluteCy /= 10; } } return new decimal((int)absoluteCy, (int)(absoluteCy >> 32), 0, isNegative, (byte)scale); } public static long ToOACurrency(decimal value) { return DecCalc.VarCyFromDec(ref AsMutable(ref value)); } private static bool IsValid(int flags) => (flags & ~(SignMask | ScaleMask)) == 0 && ((uint)(flags & ScaleMask) <= (28 << ScaleShift)); // Constructs a Decimal from an integer array containing a binary // representation. The bits argument must be a non-null integer // array with four elements. bits[0], bits[1], and // bits[2] contain the low, middle, and high 32 bits of the 96-bit // integer part of the Decimal. bits[3] contains the scale factor // and sign of the Decimal: bits 0-15 (the lower word) are unused and must // be zero; bits 16-23 must contain a value between 0 and 28, indicating // the power of 10 to divide the 96-bit integer part by to produce the // Decimal value; bits 24-30 are unused and must be zero; and finally bit // 31 indicates the sign of the Decimal value, 0 meaning positive and 1 // meaning negative. // // Note that there are several possible binary representations for the // same numeric value. For example, the value 1 can be represented as {1, // 0, 0, 0} (integer value 1 with a scale factor of 0) and equally well as // {1000, 0, 0, 0x30000} (integer value 1000 with a scale factor of 3). // The possible binary representations of a particular value are all // equally valid, and all are numerically equivalent. // public Decimal(int[] bits) : this((ReadOnlySpan<int>)(bits ?? throw new ArgumentNullException(nameof(bits)))) { } /// <summary> /// Initializes a new instance of <see cref="decimal"/> to a decimal value represented in binary and contained in the specified span. /// </summary> /// <param name="bits">A span of four <see cref="int"/>s containing a binary representation of a decimal value.</param> /// <exception cref="ArgumentException">The length of <paramref name="bits"/> is not 4, or the representation of the decimal value in <paramref name="bits"/> is not valid.</exception> public Decimal(ReadOnlySpan<int> bits) { if (bits.Length == 4) { int f = bits[3]; if (IsValid(f)) { _lo64 = (uint)bits[0] + ((ulong)(uint)bits[1] << 32); _hi32 = (uint)bits[2]; _flags = f; return; } } throw new ArgumentException(SR.Arg_DecBitCtor); } // Constructs a Decimal from its constituent parts. // public Decimal(int lo, int mid, int hi, bool isNegative, byte scale) { ArgumentOutOfRangeException.ThrowIfGreaterThan(scale, 28); _lo64 = (uint)lo + ((ulong)(uint)mid << 32); _hi32 = (uint)hi; _flags = ((int)scale) << 16; if (isNegative) _flags |= SignMask; } void IDeserializationCallback.OnDeserialization(object? sender) { // OnDeserialization is called after each instance of this class is deserialized. // This callback method performs decimal validation after being deserialized. if (!IsValid(_flags)) throw new SerializationException(SR.Overflow_Decimal); } // Constructs a Decimal from its constituent parts. private Decimal(int lo, int mid, int hi, int flags) { if (IsValid(flags)) { _lo64 = (uint)lo + ((ulong)(uint)mid << 32); _hi32 = (uint)hi; _flags = flags; return; } throw new ArgumentException(SR.Arg_DecBitCtor); } private Decimal(in decimal d, int flags) { this = d; _flags = flags; } /// <summary> /// Gets the scaling factor of the decimal, which is a number from 0 to 28 that represents the number of decimal digits. /// </summary> public byte Scale => (byte)(_flags >> ScaleShift); private sbyte Exponent { get { // Decimal tracks its exponent as a scale between 0 and 28. This scale is used // with the significand as `significand / 10^scale` // // The IFloatingPoint contract however follows the general IEEE 754 algorithm // which is `-1^s * b^e * (b^(1-p) * m)` // // In this algorithm // * `s` is the sign // * `b` is the radix (10 for decimal) // * `e` is the exponent // * `p` is the number of bits in the significand // * `m` is the significand itself // // For a value such as decimal.MaxValue, the significand is 79228162514264337593543950335 // and the scale is 0. Since decimal tracks 96 significand bits, the required algorithm (simplified) // gives us 7.9228162514264337593543950335 * 10^-67 * 10^e. To get back to our original value we // then need the exponent to be 95. // // For a value such as 1E-28, the significand is 1 and the scale is 28. The required algorithm (simplified) // gives us 1.0 * 10^-95 * 10^e. To get back to our original value we need the exponent to be 67. // // Given that scale is bound by 0 and 28, inclusive, the returned exponent will be between 95 // and 67, inclusive. That is between `(p - 1)` and `(p - 1) - MaxScale`. // // The generalized algorithm for converting from scale to exponent is then `exponent = 95 - scale`. sbyte exponent = (sbyte)(95 - Scale); Debug.Assert((exponent >= 67) && (exponent <= 95)); return exponent; } } // Adds two Decimal values. // public static decimal Add(decimal d1, decimal d2) { DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), false); return d1; } // Rounds a Decimal to an integer value. The Decimal argument is rounded // towards positive infinity. public static decimal Ceiling(decimal d) { int flags = d._flags; if ((flags & ScaleMask) != 0) DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToPositiveInfinity); return d; } // Compares two Decimal values, returning an integer that indicates their // relationship. // public static int Compare(decimal d1, decimal d2) { return DecCalc.VarDecCmp(in d1, in d2); } // Compares this object to another object, returning an integer that // indicates the relationship. // Returns a value less than zero if this object // null is considered to be less than any instance. // If object is not of type Decimal, this method throws an ArgumentException. // public int CompareTo(object? value) { if (value == null) return 1; if (!(value is decimal)) throw new ArgumentException(SR.Arg_MustBeDecimal); decimal other = (decimal)value; return DecCalc.VarDecCmp(in this, in other); } public int CompareTo(decimal value) { return DecCalc.VarDecCmp(in this, in value); } // Divides two Decimal values. // public static decimal Divide(decimal d1, decimal d2) { DecCalc.VarDecDiv(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } // Checks if this Decimal is equal to a given object. Returns true // if the given object is a boxed Decimal and its value is equal to the // value of this Decimal. Returns false otherwise. // public override bool Equals([NotNullWhen(true)] object? value) => value is decimal other && DecCalc.Equals(in this, in other); public bool Equals(decimal value) => DecCalc.Equals(in this, in value); // Returns the hash code for this Decimal. // public override int GetHashCode() => DecCalc.GetHashCode(in this); // Compares two Decimal values for equality. Returns true if the two // Decimal values are equal, or false if they are not equal. // public static bool Equals(decimal d1, decimal d2) { return DecCalc.Equals(in d1, in d2); } // Rounds a Decimal to an integer value. The Decimal argument is rounded // towards negative infinity. // public static decimal Floor(decimal d) { int flags = d._flags; if ((flags & ScaleMask) != 0) DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToNegativeInfinity); return d; } // Converts this Decimal to a string. The resulting string consists of an // optional minus sign ("-") followed to a sequence of digits ("0" - "9"), // optionally followed by a decimal point (".") and another sequence of // digits. // public override string ToString() { return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return Number.FormatDecimal(this, format, NumberFormatInfo.CurrentInfo); } public string ToString(IFormatProvider? provider) { return Number.FormatDecimal(this, null, NumberFormatInfo.GetInstance(provider)); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { return Number.FormatDecimal(this, format, NumberFormatInfo.GetInstance(provider)); } public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten); } /// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" /> public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatDecimal(this, format, NumberFormatInfo.GetInstance(provider), utf8Destination, out bytesWritten); } // Converts a string to a Decimal. The string must consist of an optional // minus sign ("-") followed by a sequence of digits ("0" - "9"). The // sequence of digits may optionally contain a single decimal point (".") // character. Leading and trailing whitespace characters are allowed. // Parse also allows a currency symbol, a trailing negative sign, and // parentheses in the number. // public static decimal Parse(string s) => Parse(s, NumberStyles.Number, provider: null); public static decimal Parse(string s, NumberStyles style) => Parse(s, style, provider: null); public static decimal Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider); public static decimal Parse(string s, NumberStyles style, IFormatProvider? provider) { if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } public static decimal Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.ParseDecimal(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out decimal result) => TryParse(s, NumberStyles.Number, provider: null, out result); public static bool TryParse(ReadOnlySpan<char> s, out decimal result) => TryParse(s, NumberStyles.Number, provider: null, out result); /// <summary>Tries to convert a UTF-8 character span containing the string representation of a number to its signed decimal equivalent.</summary> /// <param name="utf8Text">A span containing the UTF-8 characters representing the number to convert.</param> /// <param name="result">When this method returns, contains the signed decimal value equivalent to the number contained in <paramref name="utf8Text" /> if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.</param> /// <returns><c>true</c> if <paramref name="utf8Text" /> was converted successfully; otherwise, false.</returns> public static bool TryParse(ReadOnlySpan<byte> utf8Text, out decimal result) => TryParse(utf8Text, NumberStyles.Number, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out decimal result) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out decimal result) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // Returns a binary representation of a Decimal. The return value is an // integer array with four elements. Elements 0, 1, and 2 contain the low, // middle, and high 32 bits of the 96-bit integer part of the Decimal. // Element 3 contains the scale factor and sign of the Decimal: bits 0-15 // (the lower word) are unused; bits 16-23 contain a value between 0 and // 28, indicating the power of 10 to divide the 96-bit integer part by to // produce the Decimal value; bits 24-30 are unused; and finally bit 31 // indicates the sign of the Decimal value, 0 meaning positive and 1 // meaning negative. // public static int[] GetBits(decimal d) { return [(int)d.Low, (int)d.Mid, (int)d.High, d._flags]; } /// <summary> /// Converts the value of a specified instance of <see cref="decimal"/> to its equivalent binary representation. /// </summary> /// <param name="d">The value to convert.</param> /// <param name="destination">The span into which to store the four-integer binary representation.</param> /// <returns>Four, the number of integers in the binary representation.</returns> /// <exception cref="ArgumentException">The destination span was not long enough to store the binary representation.</exception> public static int GetBits(decimal d, Span<int> destination) { if (destination.Length <= 3) { ThrowHelper.ThrowArgumentException_DestinationTooShort(); } destination[0] = (int)d.Low; destination[1] = (int)d.Mid; destination[2] = (int)d.High; destination[3] = d._flags; return 4; } /// <summary> /// Tries to convert the value of a specified instance of <see cref="decimal"/> to its equivalent binary representation. /// </summary> /// <param name="d">The value to convert.</param> /// <param name="destination">The span into which to store the binary representation.</param> /// <param name="valuesWritten">The number of integers written to the destination.</param> /// <returns>true if the decimal's binary representation was written to the destination; false if the destination wasn't long enough.</returns> public static bool TryGetBits(decimal d, Span<int> destination, out int valuesWritten) { if (destination.Length <= 3) { valuesWritten = 0; return false; } destination[0] = (int)d.Low; destination[1] = (int)d.Mid; destination[2] = (int)d.High; destination[3] = d._flags; valuesWritten = 4; return true; } internal static void GetBytes(in decimal d, Span<byte> buffer) { buffer = buffer.Slice(0, 16); BinaryPrimitives.WriteInt32LittleEndian(buffer, (int)d.Low); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(4), (int)d.Mid); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(8), (int)d.High); BinaryPrimitives.WriteInt32LittleEndian(buffer.Slice(12), d._flags); } internal static decimal ToDecimal(ReadOnlySpan<byte> span) { span = span.Slice(0, 16); int lo = BinaryPrimitives.ReadInt32LittleEndian(span); int mid = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(4)); int hi = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(8)); int flags = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(12)); return new decimal(lo, mid, hi, flags); } public static decimal Remainder(decimal d1, decimal d2) { DecCalc.VarDecMod(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } // Multiplies two Decimal values. // public static decimal Multiply(decimal d1, decimal d2) { DecCalc.VarDecMul(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } // Returns the negated value of the given Decimal. If d is non-zero, // the result is -d. If d is zero, the result is zero. // public static decimal Negate(decimal d) { return new decimal(in d, d._flags ^ SignMask); } // Rounds a Decimal value to a given number of decimal places. The value // given by d is rounded to the number of decimal places given by // decimals. The decimals argument must be an integer between // 0 and 28 inclusive. // // By default a mid-point value is rounded to the nearest even number. If the mode is // passed in, it can also round away from zero. public static decimal Round(decimal d) => Round(ref d, 0, MidpointRounding.ToEven); public static decimal Round(decimal d, int decimals) => Round(ref d, decimals, MidpointRounding.ToEven); public static decimal Round(decimal d, MidpointRounding mode) => Round(ref d, 0, mode); public static decimal Round(decimal d, int decimals, MidpointRounding mode) => Round(ref d, decimals, mode); private static decimal Round(ref decimal d, int decimals, MidpointRounding mode) { if ((uint)decimals > 28) throw new ArgumentOutOfRangeException(nameof(decimals), SR.ArgumentOutOfRange_DecimalRound); if ((uint)mode > (uint)MidpointRounding.ToPositiveInfinity) throw new ArgumentException(SR.Format(SR.Argument_InvalidEnumValue, mode, nameof(MidpointRounding)), nameof(mode)); int scale = d.Scale - decimals; if (scale > 0) DecCalc.InternalRound(ref AsMutable(ref d), (uint)scale, mode); return d; } // Subtracts two Decimal values. // public static decimal Subtract(decimal d1, decimal d2) { DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), true); return d1; } // Converts a Decimal to an unsigned byte. The Decimal value is rounded // towards zero to the nearest integer value, and the result of this // operation is returned as a byte. // public static byte ToByte(decimal value) { uint temp; try { temp = ToUInt32(value); } catch (OverflowException) { Number.ThrowOverflowException<byte>(); throw; } if (temp != (byte)temp) Number.ThrowOverflowException<byte>(); return (byte)temp; } // Converts a Decimal to a signed byte. The Decimal value is rounded // towards zero to the nearest integer value, and the result of this // operation is returned as a byte. // [CLSCompliant(false)] public static sbyte ToSByte(decimal value) { int temp; try { temp = ToInt32(value); } catch (OverflowException) { Number.ThrowOverflowException<sbyte>(); throw; } if (temp != (sbyte)temp) Number.ThrowOverflowException<sbyte>(); return (sbyte)temp; } // Converts a Decimal to a short. The Decimal value is // rounded towards zero to the nearest integer value, and the result of // this operation is returned as a short. // public static short ToInt16(decimal value) { int temp; try { temp = ToInt32(value); } catch (OverflowException) { Number.ThrowOverflowException<short>(); throw; } if (temp != (short)temp) Number.ThrowOverflowException<short>(); return (short)temp; } // Converts a Decimal to a double. Since a double has fewer significant // digits than a Decimal, this operation may produce round-off errors. // public static double ToDouble(decimal d) { return DecCalc.VarR8FromDec(in d); } // Converts a Decimal to an integer. The Decimal value is rounded towards // zero to the nearest integer value, and the result of this operation is // returned as an integer. // public static int ToInt32(decimal d) { Truncate(ref d); if ((d.High | d.Mid) == 0) { int i = (int)d.Low; if (!IsNegative(d)) { if (i >= 0) return i; } else { i = -i; if (i <= 0) return i; } } throw new OverflowException(SR.Overflow_Int32); } // Converts a Decimal to a long. The Decimal value is rounded towards zero // to the nearest integer value, and the result of this operation is // returned as a long. // public static long ToInt64(decimal d) { Truncate(ref d); if (d.High == 0) { long l = (long)d.Low64; if (!IsNegative(d)) { if (l >= 0) return l; } else { l = -l; if (l <= 0) return l; } } throw new OverflowException(SR.Overflow_Int64); } // Converts a Decimal to an ushort. The Decimal // value is rounded towards zero to the nearest integer value, and the // result of this operation is returned as an ushort. // [CLSCompliant(false)] public static ushort ToUInt16(decimal value) { uint temp; try { temp = ToUInt32(value); } catch (OverflowException) { Number.ThrowOverflowException<ushort>(); throw; } if (temp != (ushort)temp) Number.ThrowOverflowException<ushort>(); return (ushort)temp; } // Converts a Decimal to an unsigned integer. The Decimal // value is rounded towards zero to the nearest integer value, and the // result of this operation is returned as an unsigned integer. // [CLSCompliant(false)] public static uint ToUInt32(decimal d) { Truncate(ref d); if ((d.High | d.Mid) == 0) { uint i = d.Low; if (!IsNegative(d) || i == 0) return i; } throw new OverflowException(SR.Overflow_UInt32); } // Converts a Decimal to an unsigned long. The Decimal // value is rounded towards zero to the nearest integer value, and the // result of this operation is returned as a long. // [CLSCompliant(false)] public static ulong ToUInt64(decimal d) { Truncate(ref d); if (d.High == 0) { ulong l = d.Low64; if (!IsNegative(d) || l == 0) return l; } throw new OverflowException(SR.Overflow_UInt64); } // Converts a Decimal to a float. Since a float has fewer significant // digits than a Decimal, this operation may produce round-off errors. // public static float ToSingle(decimal d) { return DecCalc.VarR4FromDec(in d); } // Truncates a Decimal to an integer value. The Decimal argument is rounded // towards zero to the nearest integer value, corresponding to removing all // digits after the decimal point. // public static decimal Truncate(decimal d) { Truncate(ref d); return d; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Truncate(ref decimal d) { int flags = d._flags; if ((flags & ScaleMask) != 0) DecCalc.InternalRound(ref AsMutable(ref d), (byte)(flags >> ScaleShift), MidpointRounding.ToZero); } public static implicit operator decimal(byte value) => new decimal((uint)value); [CLSCompliant(false)] public static implicit operator decimal(sbyte value) => new decimal(value); public static implicit operator decimal(short value) => new decimal(value); [CLSCompliant(false)] public static implicit operator decimal(ushort value) => new decimal((uint)value); public static implicit operator decimal(char value) => new decimal((uint)value); public static implicit operator decimal(int value) => new decimal(value); [CLSCompliant(false)] public static implicit operator decimal(uint value) => new decimal(value); public static implicit operator decimal(long value) => new decimal(value); [CLSCompliant(false)] public static implicit operator decimal(ulong value) => new decimal(value); public static explicit operator decimal(float value) => new decimal(value); public static explicit operator decimal(double value) => new decimal(value); public static explicit operator byte(decimal value) => ToByte(value); [CLSCompliant(false)] public static explicit operator sbyte(decimal value) => ToSByte(value); public static explicit operator char(decimal value) { ushort temp; try { temp = ToUInt16(value); } catch (OverflowException e) { throw new OverflowException(SR.Overflow_Char, e); } return (char)temp; } public static explicit operator short(decimal value) => ToInt16(value); [CLSCompliant(false)] public static explicit operator ushort(decimal value) => ToUInt16(value); public static explicit operator int(decimal value) => ToInt32(value); [CLSCompliant(false)] public static explicit operator uint(decimal value) => ToUInt32(value); public static explicit operator long(decimal value) => ToInt64(value); [CLSCompliant(false)] public static explicit operator ulong(decimal value) => ToUInt64(value); public static explicit operator float(decimal value) => DecCalc.VarR4FromDec(in value); public static explicit operator double(decimal value) => DecCalc.VarR8FromDec(in value); public static decimal operator +(decimal d) => d; public static decimal operator -(decimal d) => new decimal(in d, d._flags ^ SignMask); /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" /> public static decimal operator ++(decimal d) => Add(d, One); /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" /> public static decimal operator --(decimal d) => Subtract(d, One); public static decimal operator +(decimal d1, decimal d2) { DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), false); return d1; } public static decimal operator -(decimal d1, decimal d2) { DecCalc.DecAddSub(ref AsMutable(ref d1), ref AsMutable(ref d2), true); return d1; } /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" /> public static decimal operator *(decimal d1, decimal d2) { DecCalc.VarDecMul(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_Division(TSelf, TOther)" /> public static decimal operator /(decimal d1, decimal d2) { DecCalc.VarDecDiv(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } /// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" /> public static decimal operator %(decimal d1, decimal d2) { DecCalc.VarDecMod(ref AsMutable(ref d1), ref AsMutable(ref d2)); return d1; } /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)" /> public static bool operator ==(decimal d1, decimal d2) => DecCalc.Equals(in d1, in d2); /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)" /> public static bool operator !=(decimal d1, decimal d2) => !DecCalc.Equals(in d1, in d2); /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)" /> public static bool operator <(decimal d1, decimal d2) => DecCalc.VarDecCmp(in d1, in d2) < 0; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)" /> public static bool operator <=(decimal d1, decimal d2) => DecCalc.VarDecCmp(in d1, in d2) <= 0; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)" /> public static bool operator >(decimal d1, decimal d2) => DecCalc.VarDecCmp(in d1, in d2) > 0; /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)" /> public static bool operator >=(decimal d1, decimal d2) => DecCalc.VarDecCmp(in d1, in d2) >= 0; // // IConvertible implementation // public TypeCode GetTypeCode() { return TypeCode.Decimal; } bool IConvertible.ToBoolean(IFormatProvider? provider) { return Convert.ToBoolean(this); } char IConvertible.ToChar(IFormatProvider? provider) { throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "Char")); } sbyte IConvertible.ToSByte(IFormatProvider? provider) { return Convert.ToSByte(this); } byte IConvertible.ToByte(IFormatProvider? provider) { return Convert.ToByte(this); } short IConvertible.ToInt16(IFormatProvider? provider) { return Convert.ToInt16(this); } ushort IConvertible.ToUInt16(IFormatProvider? provider) { return Convert.ToUInt16(this); } int IConvertible.ToInt32(IFormatProvider? provider) { return Convert.ToInt32(this); } uint IConvertible.ToUInt32(IFormatProvider? provider) { return Convert.ToUInt32(this); } long IConvertible.ToInt64(IFormatProvider? provider) { return Convert.ToInt64(this); } ulong IConvertible.ToUInt64(IFormatProvider? provider) { return Convert.ToUInt64(this); } float IConvertible.ToSingle(IFormatProvider? provider) { return DecCalc.VarR4FromDec(in this); } double IConvertible.ToDouble(IFormatProvider? provider) { return DecCalc.VarR8FromDec(in this); } decimal IConvertible.ToDecimal(IFormatProvider? provider) { return this; } DateTime IConvertible.ToDateTime(IFormatProvider? provider) { throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Decimal", "DateTime")); } object IConvertible.ToType(Type type, IFormatProvider? provider) { return Convert.DefaultToType((IConvertible)this, type, provider); } // // IAdditiveIdentity // /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" /> static decimal IAdditiveIdentity<decimal, decimal>.AdditiveIdentity => AdditiveIdentity; // // IFloatingPoint // /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToInteger{TInteger}(TSelf)" /> public static TInteger ConvertToInteger<TInteger>(decimal value) where TInteger : IBinaryInteger<TInteger> => TInteger.CreateSaturating(value); /// <inheritdoc cref="IFloatingPoint{TSelf}.ConvertToIntegerNative{TInteger}(TSelf)" /> public static TInteger ConvertToIntegerNative<TInteger>(decimal value) where TInteger : IBinaryInteger<TInteger> => TInteger.CreateSaturating(value); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentByteCount()" /> int IFloatingPoint<decimal>.GetExponentByteCount() => sizeof(sbyte); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetExponentShortestBitLength()" /> int IFloatingPoint<decimal>.GetExponentShortestBitLength() { sbyte exponent = Exponent; return (sizeof(sbyte) * 8) - sbyte.LeadingZeroCount(exponent); } /// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandByteCount()" /> int IFloatingPoint<decimal>.GetSignificandByteCount() => sizeof(ulong) + sizeof(uint); /// <inheritdoc cref="IFloatingPoint{TSelf}.GetSignificandBitLength()" /> int IFloatingPoint<decimal>.GetSignificandBitLength() => 96; /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentBigEndian(Span{byte}, out int)" /> bool IFloatingPoint<decimal>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten) { if (destination.Length >= sizeof(sbyte)) { destination[0] = (byte)Exponent; bytesWritten = sizeof(sbyte); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteExponentLittleEndian(Span{byte}, out int)" /> bool IFloatingPoint<decimal>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten) { if (destination.Length >= sizeof(sbyte)) { destination[0] = (byte)Exponent; bytesWritten = sizeof(sbyte); return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandBigEndian(Span{byte}, out int)" /> bool IFloatingPoint<decimal>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten) { if (destination.Length >= (sizeof(uint) + sizeof(ulong))) { BinaryPrimitives.WriteUInt32BigEndian(destination, _hi32); BinaryPrimitives.WriteUInt64BigEndian(destination.Slice(sizeof(uint)), _lo64); bytesWritten = sizeof(uint) + sizeof(ulong); return true; } else { bytesWritten = 0; return false; } } /// <inheritdoc cref="IFloatingPoint{TSelf}.TryWriteSignificandLittleEndian(Span{byte}, out int)" /> bool IFloatingPoint<decimal>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten) { if (destination.Length >= (sizeof(ulong) + sizeof(uint))) { BinaryPrimitives.WriteUInt64LittleEndian(destination, _lo64); BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(sizeof(ulong)), _hi32); bytesWritten = sizeof(ulong) + sizeof(uint); return true; } else { bytesWritten = 0; return false; } } // // IFloatingPointConstants // /// <inheritdoc cref="IFloatingPointConstants{TSelf}.E" /> static decimal IFloatingPointConstants<decimal>.E => 2.7182818284590452353602874714m; /// <inheritdoc cref="IFloatingPointConstants{TSelf}.Pi" /> static decimal IFloatingPointConstants<decimal>.Pi => 3.1415926535897932384626433833m; /// <inheritdoc cref="IFloatingPointConstants{TSelf}.Tau" /> static decimal IFloatingPointConstants<decimal>.Tau => 6.2831853071795864769252867666m; // // IMinMaxValue // /// <inheritdoc cref="IMinMaxValue{TSelf}.MinValue" /> static decimal IMinMaxValue<decimal>.MinValue => MinValue; /// <inheritdoc cref="IMinMaxValue{TSelf}.MaxValue" /> static decimal IMinMaxValue<decimal>.MaxValue => MaxValue; // // IMultiplicativeIdentity // /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" /> static decimal IMultiplicativeIdentity<decimal, decimal>.MultiplicativeIdentity => MultiplicativeIdentity; // // INumber // /// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" /> public static decimal Clamp(decimal value, decimal min, decimal max) => Math.Clamp(value, min, max); /// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" /> public static decimal CopySign(decimal value, decimal sign) { return new decimal(in value, (value._flags & ~SignMask) | (sign._flags & SignMask)); } /// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" /> public static decimal Max(decimal x, decimal y) { return DecCalc.VarDecCmp(in x, in y) >= 0 ? x : y; } /// <inheritdoc cref="INumber{TSelf}.MaxNumber(TSelf, TSelf)" /> static decimal INumber<decimal>.MaxNumber(decimal x, decimal y) => Max(x, y); /// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" /> public static decimal Min(decimal x, decimal y) { return DecCalc.VarDecCmp(in x, in y) < 0 ? x : y; } /// <inheritdoc cref="INumber{TSelf}.MinNumber(TSelf, TSelf)" /> static decimal INumber<decimal>.MinNumber(decimal x, decimal y) => Min(x, y); /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" /> public static int Sign(decimal d) => (d.Low64 | d.High) == 0 ? 0 : (d._flags >> 31) | 1; // // INumberBase // /// <inheritdoc cref="INumberBase{TSelf}.One" /> static decimal INumberBase<decimal>.One => One; /// <inheritdoc cref="INumberBase{TSelf}.Radix" /> static int INumberBase<decimal>.Radix => 10; /// <inheritdoc cref="INumberBase{TSelf}.Zero" /> static decimal INumberBase<decimal>.Zero => Zero; /// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" /> public static decimal Abs(decimal value) { return new decimal(in value, value._flags & ~SignMask); } /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal CreateChecked<TOther>(TOther value) where TOther : INumberBase<TOther> { decimal result; if (typeof(TOther) == typeof(decimal)) { result = (decimal)(object)value; } else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal CreateSaturating<TOther>(TOther value) where TOther : INumberBase<TOther> { decimal result; if (typeof(TOther) == typeof(decimal)) { result = (decimal)(object)value; } else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToSaturating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal CreateTruncating<TOther>(TOther value) where TOther : INumberBase<TOther> { decimal result; if (typeof(TOther) == typeof(decimal)) { result = (decimal)(object)value; } else if (!TryConvertFrom(value, out result) && !TOther.TryConvertToTruncating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" /> public static bool IsCanonical(decimal value) { if ((value._flags & ScaleMask) == 0) { // We have an exact integer represented with no trailing zero return true; } // We have some value where some fractional part is specified. So, // if the least significant digit is 0, then we are not canonical ulong tmp = value._lo64; if (value._hi32 != 0) { // The magnitude is high64 * 2^32 + low32. Since (a * b + c) % 10 depends only on a % 10, // reduce high64 first, then fold in low32. tmp = ((ulong)value._hi32 << 32) | (tmp >> 32); tmp %= 10; tmp = (tmp << 32) | (uint)value._lo64; } return (tmp % 10) != 0; } /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" /> static bool INumberBase<decimal>.IsComplexNumber(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" /> public static bool IsEvenInteger(decimal value) { decimal truncatedValue = Truncate(value); return (value == truncatedValue) && ((truncatedValue._lo64 & 1) == 0); } /// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" /> static bool INumberBase<decimal>.IsFinite(decimal value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" /> static bool INumberBase<decimal>.IsImaginaryNumber(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" /> static bool INumberBase<decimal>.IsInfinity(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" /> public static bool IsInteger(decimal value) => value == Truncate(value); /// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" /> static bool INumberBase<decimal>.IsNaN(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" /> public static bool IsNegative(decimal value) => value._flags < 0; /// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" /> static bool INumberBase<decimal>.IsNegativeInfinity(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" /> static bool INumberBase<decimal>.IsNormal(decimal value) => (value._hi32 | value._lo64) != 0; /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" /> public static bool IsOddInteger(decimal value) { decimal truncatedValue = Truncate(value); return (value == truncatedValue) && ((truncatedValue._lo64 & 1) != 0); } /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" /> public static bool IsPositive(decimal value) => value._flags >= 0; /// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" /> static bool INumberBase<decimal>.IsPositiveInfinity(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" /> static bool INumberBase<decimal>.IsRealNumber(decimal value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" /> static bool INumberBase<decimal>.IsSubnormal(decimal value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" /> static bool INumberBase<decimal>.IsZero(decimal value) => (value._hi32 | value._lo64) == 0; /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" /> public static decimal MaxMagnitude(decimal x, decimal y) { int c = DecCalc.VarDecCmp(Abs(x), Abs(y)); if (c > 0) { return x; } if (c == 0) { return IsNegative(x) ? y : x; } return y; } /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" /> static decimal INumberBase<decimal>.MaxMagnitudeNumber(decimal x, decimal y) => MaxMagnitude(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" /> public static decimal MinMagnitude(decimal x, decimal y) { int c = DecCalc.VarDecCmp(Abs(x), Abs(y)); if (c < 0) { return x; } if (c == 0) { return IsNegative(x) ? x : y; } return y; } /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" /> static decimal INumberBase<decimal>.MinMagnitudeNumber(decimal x, decimal y) => MinMagnitude(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" /> static decimal INumberBase<decimal>.MultiplyAddEstimate(decimal left, decimal right, decimal addend) => (left * right) + addend; /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertFromChecked<TOther>(TOther value, out decimal result) => TryConvertFromChecked(value, out result); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFromChecked<TOther>(TOther value, out decimal result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `decimal` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(byte)) { byte actualValue = (byte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(char)) { char actualValue = (char)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualValue = (ushort)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualValue = (uint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualValue = (ulong)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(UInt128)) { UInt128 actualValue = (UInt128)(object)value; result = checked((decimal)actualValue); return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualValue = (nuint)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertFromSaturating<TOther>(TOther value, out decimal result) { return TryConvertFrom(value, out result); } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertFromTruncating<TOther>(TOther value, out decimal result) { return TryConvertFrom(value, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFrom<TOther>(TOther value, out decimal result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `decimal` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(byte)) { byte actualValue = (byte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(char)) { char actualValue = (char)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualValue = (ushort)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualValue = (uint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualValue = (ulong)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(UInt128)) { UInt128 actualValue = (UInt128)(object)value; result = actualValue.Upper > uint.MaxValue ? MaxValue : (decimal)actualValue; return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualValue = (nuint)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertToChecked<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result) { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `decimal` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(double)) { double actualResult = checked((double)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Half)) { Half actualResult = checked((Half)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(short)) { short actualResult = checked((short)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(int)) { int actualResult = checked((int)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(long)) { long actualResult = checked((long)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualResult = checked((Int128)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nint)) { nint actualResult = checked((nint)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualResult = checked((sbyte)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(float)) { float actualResult = checked((float)value); result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertToSaturating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result) { return TryConvertTo(value, out result); } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<decimal>.TryConvertToTruncating<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result) { return TryConvertTo(value, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertTo<TOther>(decimal value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `decimal` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(double)) { double actualResult = (double)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Half)) { Half actualResult = (Half)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(short)) { short actualResult = (value >= short.MaxValue) ? short.MaxValue : (value <= short.MinValue) ? short.MinValue : (short)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(int)) { int actualResult = (value >= int.MaxValue) ? int.MaxValue : (value <= int.MinValue) ? int.MinValue : (int)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(long)) { long actualResult = (value >= long.MaxValue) ? long.MaxValue : (value <= long.MinValue) ? long.MinValue : (long)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualResult = (Int128)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nint)) { nint actualResult = (value >= nint.MaxValue) ? nint.MaxValue : (value <= nint.MinValue) ? nint.MinValue : (nint)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualResult = (value >= sbyte.MaxValue) ? sbyte.MaxValue : (value <= sbyte.MinValue) ? sbyte.MinValue : (sbyte)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(float)) { float actualResult = (float)value; result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(string, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out decimal result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(s.AsSpan(), style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out decimal result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(s, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out decimal result, out int bytesConsumed) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(utf8Text, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; } // // IParsable // /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" /> public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out decimal result) => TryParse(s, NumberStyles.Number, provider, out result); // // ISignedNumber // /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" /> static decimal ISignedNumber<decimal>.NegativeOne => NegativeOne; // // ISpanParsable // /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" /> public static decimal Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider); /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out decimal result) => TryParse(s, NumberStyles.Number, provider, out result); // // IUtf8SpanParsable // /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" /> public static decimal Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.ParseDecimal(utf8Text, style, NumberFormatInfo.GetInstance(provider)); } /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out decimal result) { NumberFormatInfo.ValidateParseStyleDecimal(style); return Number.TryParseDecimal(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" /> public static decimal Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Number, provider); /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out decimal result) => TryParse(utf8Text, NumberStyles.Number, provider, out result); } }