diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs
index 44c1d47d96dcab..325f752ec625c2 100644
--- a/src/libraries/Common/src/System/Number.NumberBuffer.cs
+++ b/src/libraries/Common/src/System/Number.NumberBuffer.cs
@@ -21,6 +21,9 @@ internal static partial class Number
internal const int UInt32NumberBufferLength = 10 + 1; // 10 for the longest input: 4,294,967,295
internal const int UInt64NumberBufferLength = 20 + 1; // 20 for the longest input: 18,446,744,073,709,551,615
internal const int UInt128NumberBufferLength = 39 + 1; // 39 for the longest input: 340,282,366,920,938,463,463,374,607,431,768,211,455
+ internal const int Decimal32NumberBufferLength = 97 + 1 + 1; // 97 for the longest input + 1 for rounding
+ internal const int Decimal64NumberBufferLength = 385 + 1 + 1; // 385 for the longest input + 1 for rounding
+ internal const int Decimal128NumberBufferLength = 6145 + 1 + 1; // 6145 for the longest input + 1 for rounding
internal unsafe ref struct NumberBuffer
{
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index f511ebeb21810d..915b1795abd9fc 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -534,6 +534,15 @@
Object must be of type Decimal.
+
+ Object must be of type Decimal32.
+
+
+ Object must be of type Decimal64.
+
+
+ Object must be of type Decimal128.
+
Type must derive from Delegate.
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 56fdc095a74eed..1c3d93a2bfcbb0 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -436,6 +436,10 @@
+
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs
index 4c5fd32227de25..c1a031abfd5dd8 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.BigInteger.cs
@@ -1352,6 +1352,31 @@ public readonly ulong ToUInt64()
return 0;
}
+ public UInt128 ToUInt128()
+ {
+ if (_length > 3)
+ {
+ return new UInt128(((ulong)_blocks[3] << 32) + _blocks[2], ((ulong)(_blocks[1]) << 32) + _blocks[0]);
+ }
+
+ if (_length > 2)
+ {
+ return new UInt128((ulong)_blocks[2], ((ulong)_blocks[1] << 32) + _blocks[0]);
+ }
+
+ if (_length > 1)
+ {
+ return ((ulong)(_blocks[1]) << 32) + _blocks[0];
+ }
+
+ if (_length > 0)
+ {
+ return _blocks[0];
+ }
+
+ return 0;
+ }
+
private void Clear(int length) => ((Span)_blocks).Slice(0, length).Clear();
private static int DivRem32(int value, out int remainder)
diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs
new file mode 100644
index 00000000000000..ae9e4202abb606
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs
@@ -0,0 +1,441 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Numerics;
+
+namespace System
+{
+ internal static partial class Number
+ {
+ ///
+ /// Encodes the given IEEE 754 decimal components into their binary IEEE 754
+ /// decimal interchange format (BID), handles rounding/infinitive cases, producing the final bit pattern.
+ ///
+ ///
+ /// The sign of the value. true indicates a negative number; otherwise, false.
+ ///
+ ///
+ /// The fully decoded significand (coefficient):
+ /// - This is the complete integer coefficient with no packed BID encoding.
+ /// - It includes all significant digits (non-trailing).
+ /// - It has not been scaled by the exponent.
+ ///
+ ///
+ /// The unbiased exponent (actual exponent as defined by IEEE 754).
+ /// This value has already been adjusted by subtracting the format's exponent bias,
+ /// and will be re-biased internally when constructing the BID bit pattern.
+ ///
+ ///
+ /// The 32-bit or 64-bit or 128-bit IEEE 754 decimal BID encoding (depending on ),
+ /// containing the sign bit, combination field, biased exponent, and coefficient continuation bits.
+ ///
+ internal static TValue ConstructorToDecimalIeee754Bits(bool signed, TValue significand, int exponent)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ if (TValue.IsZero(significand))
+ {
+ return signed ? TDecimal.NegativeZero : TDecimal.Zero;
+ }
+
+ if (significand > TDecimal.MaxSignificand || exponent > TDecimal.MaxExponent || exponent < TDecimal.MinExponent)
+ {
+ return ConstructorToDecimalIeee754BitsRounding(signed, significand, exponent);
+ }
+
+ return DecimalIeee754FiniteNumberBinaryEncoding(signed, significand, exponent);
+
+ // This method adjusts the significand and exponent to ensure they fall within valid bounds.
+ // It handles underflow and overflow of the exponent by trimming or padding digits accordingly,
+ // and applies rounding when the number of digits exceeds the allowed precision.
+ static TValue ConstructorToDecimalIeee754BitsRounding(bool signed, TValue significand, int exponent)
+ {
+ int numberDigits = TDecimal.CountDigits(significand);
+
+ if (exponent < TDecimal.MinExponent)
+ {
+ int numberDigitsRemove = (TDecimal.MinExponent - exponent);
+
+ if (numberDigitsRemove >= numberDigits)
+ {
+ return TDecimal.Zero;
+ }
+
+ significand = RemoveDigitsAndRound(significand, numberDigitsRemove);
+ exponent += numberDigitsRemove;
+
+ if (significand > TDecimal.MaxSignificand)
+ {
+ return ConstructorToDecimalIeee754BitsRounding(signed, TDecimal.MaxSignificand + TValue.One, exponent);
+ }
+ }
+ else if (exponent > TDecimal.MaxExponent)
+ {
+ int numberZeroDigits = exponent - TDecimal.MaxExponent;
+
+ if (numberDigits + numberZeroDigits <= TDecimal.Precision)
+ {
+ exponent -= numberZeroDigits;
+ significand *= TDecimal.Power10(numberZeroDigits);
+ }
+ else
+ {
+ return signed ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity;
+ }
+ }
+ else if (numberDigits > TDecimal.Precision)
+ {
+ int numberDigitsRemove = numberDigits - TDecimal.Precision;
+
+ if (exponent + numberDigitsRemove >= TDecimal.MaxExponent)
+ {
+ return signed ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity;
+ }
+
+ significand = RemoveDigitsAndRound(significand, numberDigitsRemove);
+ exponent += numberDigitsRemove;
+
+ if (significand > TDecimal.MaxSignificand)
+ {
+ return ConstructorToDecimalIeee754BitsRounding(signed, TDecimal.MaxSignificand + TValue.One, exponent);
+ }
+ }
+
+ return DecimalIeee754FiniteNumberBinaryEncoding(signed, significand, exponent);
+ }
+
+ static TValue RemoveDigitsAndRound(TValue significand, int numberDigitsRemove)
+ {
+ (significand, TValue remainder) = TDecimal.DivRemPow10(significand, numberDigitsRemove);
+
+ if (remainder == TValue.Zero)
+ {
+ return significand;
+ }
+
+ TValue half = TValue.CreateTruncating(5) * TDecimal.Power10(numberDigitsRemove - 1);
+
+ if (remainder > half || (remainder == half && TValue.IsOddInteger(significand)))
+ {
+ significand += TValue.One;
+ }
+
+ return significand;
+ }
+ }
+
+ internal struct DecodedDecimalIeee754
+ where TSignificand : IBinaryInteger
+ {
+ public bool Signed { get; }
+ public int UnbiasedExponent { get; }
+
+ ///
+ /// The decoded significand (coefficient) in integer form:
+ /// - Fully decoded from the BID encoding (no combination-field or DPD/BID packing).
+ /// - Represents the normalized coefficient; includes the implicit leading digit if applicable.
+ /// - Not scaled by the (unbiased) exponent.
+ ///
+ public TSignificand Significand { get; }
+
+ public DecodedDecimalIeee754(bool signed, int unbiasedExponent, TSignificand significand)
+ {
+ Signed = signed;
+ UnbiasedExponent = unbiasedExponent;
+ Significand = significand;
+ }
+ }
+
+ internal static DecodedDecimalIeee754 UnpackDecimalIeee754(TValue decimalBits)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ bool signed = (decimalBits & TDecimal.SignMask) != TValue.Zero;
+ TValue significand;
+ int biasedExponent;
+
+ if ((decimalBits & TDecimal.G0G1Mask) == TDecimal.G0G1Mask)
+ {
+ biasedExponent = TDecimal.ConvertToExponent((decimalBits & TDecimal.G2ToGwPlus3ExponentMask) >> (TDecimal.NumberBitsSignificand + 1));
+ significand = (decimalBits & TDecimal.GwPlus4SignificandMask) | TDecimal.MostSignificantBitOfSignificandMask;
+ }
+ else
+ {
+ biasedExponent = TDecimal.ConvertToExponent((decimalBits & TDecimal.G0ToGwPlus1ExponentMask) >> (TDecimal.NumberBitsSignificand + 3));
+ significand = decimalBits & TDecimal.GwPlus2ToGwPlus4SignificandMask;
+ }
+
+ return new DecodedDecimalIeee754(signed, biasedExponent - TDecimal.ExponentBias, significand);
+ }
+
+ internal static int CompareDecimalIeee754(TValue currentDecimalBits, TValue otherDecimalBits)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ if (currentDecimalBits == otherDecimalBits)
+ {
+ return 0;
+ }
+
+ bool isCurrentNaN = TDecimal.IsNaN(currentDecimalBits);
+ bool isOtherNaN = TDecimal.IsNaN(otherDecimalBits);
+
+ if (isCurrentNaN || isOtherNaN)
+ {
+ if (isCurrentNaN && isOtherNaN)
+ {
+ return 0;
+ }
+ else
+ {
+ return isCurrentNaN ? -1 : 1;
+ }
+ }
+
+ bool isCurrentNegative = (currentDecimalBits & TDecimal.SignMask) != TValue.Zero;
+ bool isOtherNegative = (otherDecimalBits & TDecimal.SignMask) != TValue.Zero;
+ DecodedDecimalIeee754 current;
+ DecodedDecimalIeee754 other;
+
+ if (isCurrentNegative)
+ {
+ if (!isOtherNegative)
+ {
+ return currentDecimalBits == TDecimal.NegativeZero && otherDecimalBits == TDecimal.Zero ? 0 : -1;
+ }
+ current = UnpackDecimalIeee754(otherDecimalBits);
+ other = UnpackDecimalIeee754(currentDecimalBits);
+ }
+ else if (isOtherNegative)
+ {
+ return currentDecimalBits == TDecimal.Zero && otherDecimalBits == TDecimal.NegativeZero ? 0 : 1;
+ }
+ else
+ {
+ current = UnpackDecimalIeee754(currentDecimalBits);
+ other = UnpackDecimalIeee754(otherDecimalBits);
+ }
+
+ return InternalUnsignedCompare(current, other);
+
+ // This method is needed to correctly compare decimals that represent the same numeric value
+ // but have different exponent/significand pairs. For example, 10e2 and 1e3 have different exponents,
+ // but represent the same number (1000). This function normalizes exponents and compares them accordingly,
+ // without considering sign.
+ static int InternalUnsignedCompare(DecodedDecimalIeee754 current, DecodedDecimalIeee754 other)
+ {
+ if (current.UnbiasedExponent == other.UnbiasedExponent && current.Significand == other.Significand)
+ {
+ return 0;
+ }
+
+ if (current.UnbiasedExponent < other.UnbiasedExponent)
+ {
+ return -InternalUnsignedCompare(other, current);
+ }
+
+ if (current.Significand >= other.Significand)
+ {
+ return 1;
+ }
+
+ int diffExponent = current.UnbiasedExponent - other.UnbiasedExponent;
+ if (diffExponent < TDecimal.Precision)
+ {
+ TValue factor = TDecimal.Power10(diffExponent);
+ (TValue quotient, TValue remainder) = TValue.DivRem(other.Significand, current.Significand);
+
+ if (quotient < factor)
+ {
+ return 1;
+ }
+ if (quotient > factor)
+ {
+ return -1;
+ }
+ if (remainder > TValue.Zero)
+ {
+ return -1;
+ }
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+
+ private static TValue NumberToDecimalIeee754Bits(ref NumberBuffer number)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ Debug.Assert(number.Digits[0] != '0');
+ Debug.Assert(number.DigitsCount != 0);
+
+ if (number.DigitsCount > TDecimal.Precision)
+ {
+ return DecimalIeee754Rounding(ref number, TDecimal.Precision);
+ }
+
+ int positiveExponent = (Math.Max(0, number.Scale));
+ int integerDigitsPresent = Math.Min(positiveExponent, number.DigitsCount);
+ int fractionalDigitsPresent = number.DigitsCount - integerDigitsPresent;
+ int exponent = number.Scale - integerDigitsPresent - fractionalDigitsPresent;
+
+ if (exponent < TDecimal.MinExponent)
+ {
+ int numberDigitsRemove = (TDecimal.MinExponent - exponent);
+ if (numberDigitsRemove < number.DigitsCount)
+ {
+ int numberDigitsRemain = number.DigitsCount - numberDigitsRemove;
+ return DecimalIeee754Rounding(ref number, numberDigitsRemain);
+ }
+ else
+ {
+ return number.IsNegative ? TDecimal.NegativeZero : TDecimal.Zero;
+ }
+ }
+
+ TValue significand = TDecimal.NumberToSignificand(ref number, number.DigitsCount);
+
+ return DecimalIeee754FiniteNumberBinaryEncoding(number.IsNegative, significand, exponent);
+ }
+
+ ///
+ /// Encodes the given IEEE 754 decimal components into their finite number binary IEEE 754
+ /// decimal interchange format (BID), producing the final bit pattern.
+ ///
+ ///
+ /// The sign of the value. true indicates a negative number; otherwise, false.
+ ///
+ ///
+ /// The fully decoded significand (coefficient):
+ /// - This is the complete integer coefficient with no packed BID encoding.
+ /// - It includes all significant digits (non-trailing).
+ /// - It has not been scaled by the exponent.
+ ///
+ ///
+ /// The unbiased exponent (actual exponent as defined by IEEE 754).
+ /// This value has already been adjusted by subtracting the format's exponent bias,
+ /// and will be re-biased internally when constructing the BID bit pattern.
+ ///
+ ///
+ /// The 32-bit or 64-bit or 128-bit IEEE 754 decimal BID encoding (depending on ),
+ /// containing the sign bit, combination field, biased exponent, and coefficient continuation bits.
+ ///
+ private static TValue DecimalIeee754FiniteNumberBinaryEncoding(bool signed, TValue significand, int exponent)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ exponent += TDecimal.ExponentBias;
+
+ TValue value = TValue.Zero;
+ TValue exponentVal = TValue.CreateTruncating(exponent);
+ bool msbSignificand = (significand & TDecimal.MostSignificantBitOfSignificandMask) != TValue.Zero;
+
+ if (signed)
+ {
+ value = TDecimal.SignMask;
+ }
+
+ if (msbSignificand)
+ {
+ value |= TDecimal.G0G1Mask;
+ exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 3;
+ value |= exponentVal;
+ significand ^= TDecimal.MostSignificantBitOfSignificandMask;
+ value |= significand;
+ }
+ else
+ {
+ exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 1;
+ value |= exponentVal;
+ value |= significand;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Performs IEEE 754-compliant rounding on a decimal-like number before converting it
+ /// to an IEEE 754 decimal32/64/128 encoded value.
+ ///
+ /// ---------------------------------------------------------------
+ /// ROUNDING DECISION (implements round-to-nearest, ties-to-even)
+ ///
+ /// Unit In The Last Place (ULP) formula: ULP = 10^(unbiased exponent - number digits precision + 1)
+ /// The difference between the unrounded number and the rounded
+ /// representable value is effectively compared against ±ULP/2.
+ ///
+ /// If discarded part > 0.5 ULP → round up
+ /// If discarded part < 0.5 ULP → round down
+ /// If exactly 0.5 ULP → ties-to-even
+ ///
+ private static TValue DecimalIeee754Rounding(ref NumberBuffer number, int digits)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ Debug.Assert(digits < number.DigitsCount);
+
+ TValue significand = TDecimal.NumberToSignificand(ref number, digits);
+
+ int positiveExponent = (Math.Max(0, number.Scale));
+ int integerDigitsPresent = Math.Min(positiveExponent, number.DigitsCount);
+ int fractionalDigitsPresent = number.DigitsCount - integerDigitsPresent;
+ int exponent = number.Scale - integerDigitsPresent - fractionalDigitsPresent;
+
+ exponent += number.DigitsCount - digits;
+
+ bool increaseOne = false;
+ int midPointValue = number.Digits[digits];
+
+ if (midPointValue > '5')
+ {
+ increaseOne = true;
+ }
+ else if (midPointValue == '5')
+ {
+ int index = digits + 1;
+ int c = number.Digits[index];
+ bool tiedToEvenRounding = true;
+
+ while (index < number.DigitsCount && c != 0)
+ {
+ if (c != '0')
+ {
+ increaseOne = true;
+ tiedToEvenRounding = false;
+ break;
+ }
+ ++index;
+ c = number.Digits[index];
+ }
+
+ if (tiedToEvenRounding && !int.IsEvenInteger(number.Digits[digits - 1] - '0'))
+ {
+ increaseOne = true;
+ }
+ }
+
+ if (increaseOne)
+ {
+ if (significand == TDecimal.MaxSignificand)
+ {
+ significand = TDecimal.Power10(TDecimal.Precision - 1);
+ exponent += 1;
+ }
+ else
+ {
+ significand += TValue.One;
+ }
+ }
+
+ if (exponent > TDecimal.MaxExponent)
+ {
+ return number.IsNegative ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity;
+ }
+
+ return DecimalIeee754FiniteNumberBinaryEncoding(number.IsNegative, significand, exponent);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
index 9b2028fa0f48ff..6ba16446778077 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
@@ -1,6 +1,7 @@
// 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;
using System.Buffers.Text;
using System.Collections.Generic;
using System.Diagnostics;
@@ -329,6 +330,36 @@ private static ref byte GetTwoDigitsBytesRef(bool useChars) =>
ref MemoryMarshal.GetReference(useChars ? TwoDigitsCharsAsBytes : TwoDigitsBytes);
#endif
+ public static unsafe string FormatDecimalIeee754(TDecimal value, ReadOnlySpan format, NumberFormatInfo info)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ char fmt = ParseFormatSpecifier(format, out int digits);
+ byte[] buffer = ArrayPool.Shared.Rent(TDecimal.BufferLength);
+
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, buffer);
+
+ DecimalIeee754ToNumber(value, ref number);
+ char* stackPtr = stackalloc char[CharStackBufferSize];
+ var vlb = new ValueListBuilder(new Span(stackPtr, CharStackBufferSize));
+
+ if (fmt != 0)
+ {
+ NumberToString(ref vlb, ref number, fmt, digits, info);
+ }
+ else
+ {
+ NumberToStringFormat(ref vlb, ref number, format, info);
+ }
+
+ string result = vlb.AsSpan().ToString();
+ vlb.Dispose();
+ if (buffer != null)
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ return result;
+ }
public static unsafe string FormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info)
{
@@ -384,6 +415,46 @@ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan(TDecimal value, ref NumberBuffer number)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ DecodedDecimalIeee754 unpackDecimal = value.Unpack();
+ number.IsNegative = unpackDecimal.Signed;
+
+ if (unpackDecimal.Significand == TValue.Zero)
+ {
+ return;
+ }
+
+ string significand = TDecimal.ToDecStr(unpackDecimal.Significand);
+
+ for (int i = 0; i < significand.Length; i++)
+ {
+ number.Digits[i] = (byte)significand[i];
+ }
+
+ number.Scale = TValue.IsZero(unpackDecimal.Significand) ? 0 : significand.Length + unpackDecimal.UnbiasedExponent;
+
+ if (unpackDecimal.UnbiasedExponent > 0)
+ {
+ number.DigitsCount = significand.Length + unpackDecimal.UnbiasedExponent;
+
+ for (int i = 0; i < unpackDecimal.UnbiasedExponent; i++)
+ {
+ number.Digits[significand.Length + i] = (byte)'0';
+ }
+ }
+ else
+ {
+ number.DigitsCount = significand.Length;
+ }
+
+ number.Digits[number.DigitsCount] = (byte)'\0';
+
+ number.CheckConsistency();
+ }
+
internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuffer number)
{
byte* buffer = number.DigitsPtr;
@@ -1590,7 +1661,7 @@ private static unsafe bool TryUInt32ToBinaryStr(uint value, int digits, S
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number)
+ internal static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number)
{
number.DigitsCount = UInt32Precision;
number.IsNegative = false;
@@ -2055,7 +2126,7 @@ private static unsafe bool TryUInt64ToBinaryStr(ulong value, int digits,
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number)
+ internal static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number)
{
number.DigitsCount = UInt64Precision;
number.IsNegative = false;
@@ -2467,7 +2538,7 @@ private static unsafe bool TryUInt128ToBinaryStr(Int128 value, int digits
}
}
- private static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer number)
+ internal static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer number)
{
number.DigitsCount = UInt128Precision;
number.IsNegative = false;
diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs
index 92535633232725..dda55d21a01baa 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs
@@ -696,7 +696,7 @@ internal unsafe partial class Number
0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648
];
- private static void AccumulateDecimalDigitsIntoBigInteger(scoped ref NumberBuffer number, uint firstIndex, uint lastIndex, out BigInteger result)
+ internal static void AccumulateDecimalDigitsIntoBigInteger(scoped ref NumberBuffer number, uint firstIndex, uint lastIndex, out BigInteger result)
{
BigInteger.SetZero(out result);
@@ -890,7 +890,7 @@ private static ulong ConvertBigIntegerToFloatingPointBits(ref BigInteger
}
// get 32-bit integer from at most 9 digits
- private static uint DigitsToUInt32(byte* p, int count)
+ internal static uint DigitsToUInt32(byte* p, int count)
{
Debug.Assert((1 <= count) && (count <= 9));
@@ -914,7 +914,7 @@ private static uint DigitsToUInt32(byte* p, int count)
}
// get 64-bit integer from at most 19 digits
- private static ulong DigitsToUInt64(byte* p, int count)
+ internal static ulong DigitsToUInt64(byte* p, int count)
{
Debug.Assert((1 <= count) && (count <= 19));
diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
index 3748792c1784d3..69eb2836d66786 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
@@ -133,6 +133,44 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI
static abstract int MaxPrecisionCustomFormat { get; }
}
+ internal interface IDecimalIeee754ParseAndFormatInfo
+ where TSelf : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ static abstract int Precision { get; }
+ static abstract int MaxScale { get; }
+ static abstract int MinScale { get; }
+ static abstract int BufferLength { get; }
+ static abstract int MaxExponent { get; }
+ static abstract int MinExponent { get; }
+ static abstract int ExponentBias { get; }
+ static abstract TValue PositiveInfinity { get; }
+ static abstract TValue NegativeInfinity { get; }
+ static abstract TValue NaN { get; }
+ static abstract TValue Zero { get; }
+ static abstract TValue NegativeZero { get; }
+ static abstract TValue MaxSignificand { get; }
+ static abstract TValue NumberToSignificand(ref Number.NumberBuffer number, int digits);
+ static abstract string ToDecStr(TValue significand);
+ static abstract int ConvertToExponent(TValue value);
+ static abstract TValue Power10(int exponent);
+ static abstract (TValue Quotient, TValue Remainder) DivRemPow10(TValue value, int exponent);
+ Number.DecodedDecimalIeee754 Unpack();
+ static abstract TSelf Construct(TValue value);
+ static abstract int CountDigits(TValue significand);
+ static abstract int NumberBitsEncoding { get; }
+ static abstract int NumberBitsExponent { get; }
+ static abstract int NumberBitsSignificand { get; }
+ static abstract TValue SignMask { get; }
+ static abstract TValue G0G1Mask { get; }
+ static abstract TValue G0ToGwPlus1ExponentMask { get; } //G0 to G(w+1)
+ static abstract TValue G2ToGwPlus3ExponentMask { get; } //G2 to G(w+3)
+ static abstract TValue GwPlus2ToGwPlus4SignificandMask { get; } //G(w+2) to G(w+4)
+ static abstract TValue GwPlus4SignificandMask { get; } //G(w+4)
+ static abstract TValue MostSignificantBitOfSignificandMask { get; }
+ static abstract bool IsNaN(TValue decimalBits);
+ }
+
internal static partial class Number
{
private const int Int32Precision = 10;
@@ -767,6 +805,21 @@ internal static decimal ParseDecimal(ReadOnlySpan value, NumberSty
return result;
}
+ internal static TDecimal ParseDecimalIeee754(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info)
+ where TChar : unmanaged, IUtfChar
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out TDecimal result);
+
+ if (status == ParsingStatus.Failed)
+ {
+ ThrowFormatException(value);
+ }
+
+ return result;
+ }
+
internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
{
number.CheckConsistency();
@@ -918,6 +971,87 @@ internal static ParsingStatus TryParseDecimal(ReadOnlySpan value,
return ParsingStatus.OK;
}
+ internal static ParsingStatus TryParseDecimalIeee754(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TDecimal result)
+ where TChar : unmanaged, IUtfChar
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[TDecimal.BufferLength]);
+ result = default;
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ ReadOnlySpan valueTrim = SpanTrim(value);
+ ReadOnlySpan positiveInfinitySymbol = info.PositiveInfinitySymbolTChar();
+
+ if (SpanEqualsOrdinalIgnoreCase(valueTrim, positiveInfinitySymbol))
+ {
+ result = TDecimal.Construct(TDecimal.PositiveInfinity);
+ return ParsingStatus.OK;
+ }
+
+ if (SpanEqualsOrdinalIgnoreCase(valueTrim, info.NegativeInfinitySymbolTChar()))
+ {
+ result = TDecimal.Construct(TDecimal.NegativeInfinity);
+ return ParsingStatus.OK;
+ }
+
+ ReadOnlySpan nanSymbol = info.NaNSymbolTChar();
+
+ if (SpanEqualsOrdinalIgnoreCase(valueTrim, nanSymbol))
+ {
+ result = TDecimal.Construct(TDecimal.NaN);
+ return ParsingStatus.OK;
+ }
+
+
+ var positiveSign = info.PositiveSignTChar();
+
+ if (SpanStartsWith(valueTrim, positiveSign, StringComparison.OrdinalIgnoreCase))
+ {
+ valueTrim = valueTrim.Slice(positiveSign.Length);
+
+ if (SpanEqualsOrdinalIgnoreCase(valueTrim, positiveInfinitySymbol))
+ {
+ result = TDecimal.Construct(TDecimal.PositiveInfinity);
+ return ParsingStatus.OK;
+ }
+ else if (SpanEqualsOrdinalIgnoreCase(valueTrim, nanSymbol))
+ {
+ result = TDecimal.Construct(TDecimal.NaN);
+ return ParsingStatus.OK;
+ }
+
+ result = TDecimal.Construct(TDecimal.Zero);
+ return ParsingStatus.OK;
+ }
+
+ ReadOnlySpan negativeSign = info.NegativeSignTChar();
+
+ if (SpanStartsWith(valueTrim, negativeSign, StringComparison.OrdinalIgnoreCase))
+ {
+ if (SpanEqualsOrdinalIgnoreCase(valueTrim.Slice(negativeSign.Length), nanSymbol))
+ {
+ result = TDecimal.Construct(TDecimal.NaN);
+ return ParsingStatus.OK;
+ }
+
+ if (info.AllowHyphenDuringParsing() && SpanStartsWith(valueTrim, TChar.CastFrom('-')) && SpanEqualsOrdinalIgnoreCase(valueTrim.Slice(1), nanSymbol))
+ {
+ result = TDecimal.Construct(TDecimal.NaN);
+ return ParsingStatus.OK;
+ }
+ }
+
+ result = TDecimal.Construct(TDecimal.Zero);
+ return ParsingStatus.Failed;
+ }
+
+ result = NumberToDecimalIeee754(ref number);
+
+ return ParsingStatus.OK;
+ }
+
internal static bool SpanStartsWith(ReadOnlySpan span, TChar c)
where TChar : unmanaged, IUtfChar
{
@@ -1135,5 +1269,27 @@ internal static TFloat NumberToFloat(ref NumberBuffer number)
return number.IsNegative ? -result : result;
}
+
+ internal static TDecimal NumberToDecimalIeee754(ref NumberBuffer number)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TValue : unmanaged, IBinaryInteger
+ {
+ number.CheckConsistency();
+ TValue value;
+
+ if ((number.DigitsCount == 0) || (number.Scale < TDecimal.MinScale))
+ {
+ value = number.IsNegative ? TDecimal.NegativeZero : TDecimal.Zero;
+ }
+ else if (number.Scale > TDecimal.MaxScale)
+ {
+ value = number.IsNegative ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity;
+ }
+ else
+ {
+ value = NumberToDecimalIeee754Bits(ref number);
+ }
+ return TDecimal.Construct(value);
+ }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs
new file mode 100644
index 00000000000000..63170c22c2b189
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs
@@ -0,0 +1,380 @@
+// 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.Text;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+
+namespace System.Numerics
+{
+ public readonly struct Decimal128
+ : IComparable,
+ IComparable,
+ IEquatable,
+ ISpanParsable,
+ IMinMaxValue,
+ IDecimalIeee754ParseAndFormatInfo
+ {
+#if BIGENDIAN
+ internal readonly ulong _upper;
+ internal readonly ulong _lower;
+#else
+ internal readonly ulong _lower;
+ internal readonly ulong _upper;
+#endif
+
+ private const int MaxExponent = 6111;
+ private const int MinExponent = -6176;
+ private const int Precision = 34;
+ private const int ExponentBias = 6176;
+ private const int NumberBitsExponent = 14;
+ private static UInt128 PositiveInfinityValue => new UInt128(upper: 0x7800_0000_0000_0000, lower: 0);
+ private static UInt128 NegativeInfinityValue => new UInt128(upper: 0xf800_0000_0000_0000, lower: 0);
+ private static UInt128 ZeroValue => new UInt128(0, 0);
+ private static UInt128 NegativeZeroValue => new UInt128(0x8000_0000_0000_0000, 0);
+ private static UInt128 QuietNaNValue => new UInt128(0x7C00_0000_0000_0000, 0);
+ private static UInt128 MaxInternalValue = new UInt128(upper: 0x5FFF_ED09_BEAD_87C0, lower: 0x378D_8E63_FFFF_FFFF);
+ private static UInt128 MinInternalValue = new UInt128(upper: 0xDFFF_ED09_BEAD_87C0, lower: 0x378D_8E63_FFFF_FFFF);
+
+ private const ulong NaNMaskUpper = 0x7C00_0000_0000_0000;
+
+ public static Decimal128 PositiveInfinity => new Decimal128(PositiveInfinityValue);
+ public static Decimal128 NegativeInfinity => new Decimal128(NegativeInfinityValue);
+ public static Decimal128 NaN => new Decimal128(QuietNaNValue);
+ public static Decimal128 NegativeZero => new Decimal128(NegativeZeroValue);
+ public static Decimal128 Zero => new Decimal128(ZeroValue);
+ public static Decimal128 MinValue => new Decimal128(MinInternalValue);
+ public static Decimal128 MaxValue => new Decimal128(MaxInternalValue);
+
+ internal Decimal128(UInt128 value)
+ {
+ _upper = value.Upper;
+ _lower = value.Lower;
+ }
+
+ public Decimal128(Int128 significand, int exponent)
+ {
+ UInt128 value = Number.ConstructorToDecimalIeee754Bits(significand < 0, (UInt128)(significand < 0 ? -significand : significand), exponent);
+ _upper = value.Upper;
+ _lower = value.Lower;
+ }
+
+ ///
+ /// Parses a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string. If the input exceeds Decimal128's range, a or is returned.
+ public static Decimal128 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ ///
+ /// Parses a from a in the given .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// The equivalent value representing the input string. If the input exceeds Decimal128's range, a or is returned.
+ public static Decimal128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
+
+ ///
+ public static Decimal128 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal128's range, a or is returned.
+ public static Decimal128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal128's range, a or is returned.
+ public static Decimal128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Parses a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal128's range, a or is returned.
+ public static Decimal128 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal128's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal128 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal128's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, out Decimal128 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal128's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal128's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.TryParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal128's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+
+ if (s == null)
+ {
+ result = default;
+ return false;
+ }
+ return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ public int CompareTo(object? value)
+ {
+ if (value is not Decimal128 other)
+ {
+ return (value is null) ? 1 : throw new ArgumentException(SR.Arg_MustBeDecimal128);
+ }
+ return CompareTo(other);
+ }
+
+ ///
+ public int CompareTo(Decimal128 other)
+ {
+ var current = new UInt128(_upper, _lower);
+ var another = new UInt128(other._upper, other._lower);
+ return Number.CompareDecimalIeee754(current, another);
+ }
+
+ ///
+ public bool Equals(Decimal128 other)
+ {
+ var current = new UInt128(_upper, _lower);
+ var another = new UInt128(other._upper, other._lower);
+ return Number.CompareDecimalIeee754(current, another) == 0;
+ }
+
+ ///
+ /// Returns a value that indicates whether this instance is equal to a specified .
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal128 && Equals((Decimal128)obj);
+ }
+
+ ///
+ /// Serves as the default hash function.
+ ///
+ public override int GetHashCode()
+ {
+ return new UInt128(_upper, _lower).GetHashCode();
+ }
+
+ ///
+ /// Returns a string representation of the current value.
+ ///
+ public override string ToString()
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value with the specified .
+ ///
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified and .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ private static UInt128[] UInt128Powers10 =>
+ [
+ new UInt128(0, 1),
+ new UInt128(0, 10),
+ new UInt128(0, 100),
+ new UInt128(0, 1000),
+ new UInt128(0, 10000),
+ new UInt128(0, 100000),
+ new UInt128(0, 1000000),
+ new UInt128(0, 10000000),
+ new UInt128(0, 100000000),
+ new UInt128(0, 1000000000),
+ new UInt128(0, 10000000000),
+ new UInt128(0, 100000000000),
+ new UInt128(0, 1000000000000),
+ new UInt128(0, 10000000000000),
+ new UInt128(0, 100000000000000),
+ new UInt128(0, 1000000000000000),
+ new UInt128(0, 10000000000000000),
+ new UInt128(0, 100000000000000000),
+ new UInt128(0, 1000000000000000000),
+ new UInt128(0, 10000000000000000000),
+ new UInt128(5, 7766279631452241920),
+ new UInt128(54, 3875820019684212736),
+ new UInt128(542, 1864712049423024128),
+ new UInt128(5421, 200376420520689664),
+ new UInt128(54210, 2003764205206896640),
+ new UInt128(542101, 1590897978359414784),
+ new UInt128(5421010, 15908979783594147840),
+ new UInt128(54210108, 11515845246265065472),
+ new UInt128(542101086, 4477988020393345024),
+ new UInt128(5421010862, 7886392056514347008),
+ new UInt128(54210108624, 5076944270305263616),
+ new UInt128(542101086242, 13875954555633532928),
+ new UInt128(5421010862427, 9632337040368467968),
+ new UInt128(54210108624275, 4089650035136921600),
+ new UInt128(542101086242752, 4003012203950112768),
+ ];
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 6145;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinScale => -6175;
+
+ static string IDecimalIeee754ParseAndFormatInfo.ToDecStr(UInt128 significand)
+ {
+ return Number.UInt128ToDecStr(significand);
+ }
+
+ Number.DecodedDecimalIeee754 IDecimalIeee754ParseAndFormatInfo.Unpack()
+ {
+ return Number.UnpackDecimalIeee754(new UInt128(_upper, _lower));
+ }
+
+ static unsafe UInt128 IDecimalIeee754ParseAndFormatInfo.NumberToSignificand(ref Number.NumberBuffer number, int digits)
+ {
+ if (digits <= 19)
+ {
+ return Number.DigitsToUInt64(number.DigitsPtr, digits);
+ }
+ else
+ {
+ Number.AccumulateDecimalDigitsIntoBigInteger(ref number, 0, (uint)digits, out Number.BigInteger result);
+ return result.ToUInt128();
+ }
+ }
+
+ static Decimal128 IDecimalIeee754ParseAndFormatInfo.Construct(UInt128 value) => new Decimal128(value);
+
+ static int IDecimalIeee754ParseAndFormatInfo.ConvertToExponent(UInt128 value) => (int)value;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.Power10(int exponent) => UInt128Powers10[exponent];
+
+ static (UInt128 Quotient, UInt128 Remainder) IDecimalIeee754ParseAndFormatInfo.DivRemPow10(UInt128 value, int exponent)
+ {
+ UInt128 power = UInt128Powers10[exponent];
+ return UInt128.DivRem(value, power);
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.CountDigits(UInt128 significand) => FormattingHelpers.CountDigits(significand);
+
+ static int IDecimalIeee754ParseAndFormatInfo.Precision => Precision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.BufferLength => Number.Decimal128NumberBufferLength;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxExponent => MaxExponent;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinExponent => MinExponent;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.PositiveInfinity => PositiveInfinityValue;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.NegativeInfinity => NegativeInfinityValue;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.Zero => ZeroValue;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.NegativeZero => NegativeZeroValue;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.NaN => QuietNaNValue;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.MostSignificantBitOfSignificandMask => new UInt128(0x0002_0000_0000_0000, 0);
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsEncoding => 128;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.SignMask => new UInt128(0x8000_0000_0000_0000, 0);
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.G0G1Mask => new UInt128(0x6000_0000_0000_0000, 0);
+
+ static int IDecimalIeee754ParseAndFormatInfo.ExponentBias => ExponentBias;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsSignificand => 110;
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.G0ToGwPlus1ExponentMask => new UInt128(0x7FFE_0000_0000_0000, 0);
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.G2ToGwPlus3ExponentMask => new UInt128(0x1FFF_8000_0000_0000, 0);
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.GwPlus2ToGwPlus4SignificandMask => new UInt128(0x0001_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.GwPlus4SignificandMask => new UInt128(0x0000_7FFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ static UInt128 IDecimalIeee754ParseAndFormatInfo.MaxSignificand => new UInt128(upper: 0x0001_ED09_BEAD_87C0, lower: 0x378D_8E63_FFFF_FFFF); // 9_999_999_999_999_999_999_999_999_999_999_999;
+
+ static bool IDecimalIeee754ParseAndFormatInfo.IsNaN(UInt128 decimalBits)
+ {
+ return (decimalBits.Upper & NaNMaskUpper) == NaNMaskUpper;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs
new file mode 100644
index 00000000000000..376147b01c22c1
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs
@@ -0,0 +1,341 @@
+// 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.Text;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace System.Numerics
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly struct Decimal32
+ : IComparable,
+ IComparable,
+ IEquatable,
+ ISpanParsable,
+ IMinMaxValue,
+ IDecimalIeee754ParseAndFormatInfo
+ {
+ internal readonly uint _value;
+
+ internal Decimal32(uint value)
+ {
+ _value = value;
+ }
+
+ private const int MaxExponent = 90;
+ private const int MinExponent = -101;
+ private const int Precision = 7;
+ private const int ExponentBias = 101;
+ private const int NumberBitsExponent = 8;
+ private const uint PositiveInfinityValue = 0x7800_0000;
+ private const uint NegativeInfinityValue = 0xF800_0000;
+ private const uint ZeroValue = 0x0000_0000;
+ private const uint NegativeZeroValue = 0x8000_0000;
+ private const uint QuietNaNValue = 0x7C00_0000;
+ private const uint G0G1Mask = 0x6000_0000;
+ private const uint SignMask = 0x8000_0000;
+ private const uint MostSignificantBitOfSignificandMask = 0x0080_0000;
+ private const uint NaNMask = 0x7C00_0000;
+ private const uint MaxSignificand = 9_999_999;
+ private const uint MaxInternalValue = 0x77F8_967F; // 9,999,999 x 10^90
+ private const uint MinInternalValue = 0xF7F8_967F; // -9,999,999 x 10^90
+
+ public static Decimal32 PositiveInfinity => new Decimal32(PositiveInfinityValue);
+ public static Decimal32 NegativeInfinity => new Decimal32(NegativeInfinityValue);
+ public static Decimal32 NaN => new Decimal32(QuietNaNValue);
+ public static Decimal32 NegativeZero => new Decimal32(NegativeZeroValue);
+ public static Decimal32 Zero => new Decimal32(ZeroValue);
+ public static Decimal32 MinValue => new Decimal32(MinInternalValue);
+ public static Decimal32 MaxValue => new Decimal32(MaxInternalValue);
+
+ private static ReadOnlySpan UInt32Powers10 =>
+ [
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ ];
+
+ public Decimal32(int significand, int exponent)
+ {
+ _value = Number.ConstructorToDecimalIeee754Bits(significand < 0, (uint)Math.Abs(significand), exponent);
+ }
+
+ ///
+ /// Parses a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string. If the input exceeds Decimal32's range, a or is returned.
+ public static Decimal32 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ ///
+ /// Parses a from a in the given .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// The equivalent value representing the input string. If the input exceeds Decimal32's range, a or is returned.
+ public static Decimal32 Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
+
+ ///
+ public static Decimal32 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal32's range, a or is returned.
+ public static Decimal32 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal32's range, a or is returned.
+ public static Decimal32 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Parses a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal32's range, a or is returned.
+ public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal32's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal32 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal32's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, out Decimal32 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal32's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal32's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.TryParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal32's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+
+ if (s == null)
+ {
+ result = default;
+ return false;
+ }
+ return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ public int CompareTo(object? value)
+ {
+ if (value == null)
+ {
+ return 1;
+ }
+
+ if (value is not Decimal32 i)
+ {
+ throw new ArgumentException(SR.Arg_MustBeDecimal32);
+ }
+
+ return Number.CompareDecimalIeee754(_value, i._value);
+ }
+
+ ///
+ public int CompareTo(Decimal32 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value);
+ }
+
+ ///
+ public bool Equals(Decimal32 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value) == 0;
+ }
+
+ ///
+ /// Returns a value that indicates whether this instance is equal to a specified .
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal32 && Equals((Decimal32)obj);
+ }
+
+ ///
+ /// Serves as the default hash function.
+ ///
+ public override int GetHashCode()
+ {
+ return _value.GetHashCode();
+ }
+
+ ///
+ /// Returns a string representation of the current value.
+ ///
+ public override string ToString()
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value with the specified .
+ ///
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified and .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.Precision => Precision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.BufferLength => Number.Decimal32NumberBufferLength;
+
+ static string IDecimalIeee754ParseAndFormatInfo.ToDecStr(uint significand)
+ {
+ return Number.UInt32ToDecStr(significand);
+ }
+
+ Number.DecodedDecimalIeee754 IDecimalIeee754ParseAndFormatInfo.Unpack()
+ {
+ return Number.UnpackDecimalIeee754(_value);
+ }
+
+ static unsafe uint IDecimalIeee754ParseAndFormatInfo.NumberToSignificand(ref Number.NumberBuffer number, int digits)
+ {
+ return Number.DigitsToUInt32(number.DigitsPtr, digits);
+ }
+
+ static Decimal32 IDecimalIeee754ParseAndFormatInfo.Construct(uint value) => new Decimal32(value);
+
+ static int IDecimalIeee754ParseAndFormatInfo.ConvertToExponent(uint value) => (int)value;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.Power10(int exponent) => UInt32Powers10[exponent];
+
+ static (uint Quotient, uint Remainder) IDecimalIeee754ParseAndFormatInfo.DivRemPow10(uint value, int exponent)
+ {
+ uint power = UInt32Powers10[exponent];
+ return Math.DivRem(value, power);
+ }
+ static int IDecimalIeee754ParseAndFormatInfo.CountDigits(uint significand) => FormattingHelpers.CountDigits(significand);
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 97;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinScale => -100;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxExponent => MaxExponent;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinExponent => MinExponent;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.PositiveInfinity => PositiveInfinityValue;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.NegativeInfinity => NegativeInfinityValue;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.Zero => ZeroValue;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.NegativeZero => NegativeZeroValue;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.NaN => QuietNaNValue;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsEncoding => 32;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.SignMask => SignMask;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.G0G1Mask => G0G1Mask;
+
+ static int IDecimalIeee754ParseAndFormatInfo.ExponentBias => ExponentBias;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsSignificand => 20;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.G0ToGwPlus1ExponentMask => 0x7F80_0000;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.G2ToGwPlus3ExponentMask => 0x1FE0_0000;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.GwPlus2ToGwPlus4SignificandMask => 0x007F_FFFF;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.GwPlus4SignificandMask => 0x001F_FFFF;
+
+ static uint IDecimalIeee754ParseAndFormatInfo.MaxSignificand => MaxSignificand;
+
+ static bool IDecimalIeee754ParseAndFormatInfo.IsNaN(uint decimalBits)
+ {
+ return (decimalBits & NaNMask) == NaNMask;
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs
new file mode 100644
index 00000000000000..d343206b237e4f
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs
@@ -0,0 +1,344 @@
+// 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.Text;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+
+namespace System.Numerics
+{
+ public readonly struct Decimal64
+ : IComparable,
+ IComparable,
+ IEquatable,
+ ISpanParsable,
+ IMinMaxValue,
+ IDecimalIeee754ParseAndFormatInfo
+ {
+ internal readonly ulong _value;
+
+ private const int MaxExponent = 369;
+ private const int MinExponent = -398;
+ private const int Precision = 16;
+ private const int ExponentBias = 398;
+ private const int NumberBitsExponent = 10;
+ private const ulong PositiveInfinityValue = 0x7800_0000_0000_0000;
+ private const ulong NegativeInfinityValue = 0xF800_0000_0000_0000;
+ private const ulong ZeroValue = 0x0000_0000_0000_0000;
+ private const ulong NegativeZeroValue = 0x8000_0000_0000_0000;
+ private const ulong QuietNaNValue = 0x7C00_0000_0000_0000;
+ private const ulong G0G1Mask = 0x6000_0000_0000_0000;
+ private const ulong SignMask = 0x8000_0000_0000_0000;
+ private const ulong MostSignificantBitOfSignificandMask = 0x0020_0000_0000_0000;
+ private const ulong NaNMask = 0x7C00_0000_0000_0000;
+ private const ulong MaxSignificand = 9_999_999_999_999_999;
+ private const ulong MaxInternalValue = 0x77FB_86F2_6FC0_FFFF; // 9_999_999_999_999_999 x 10^369
+ private const ulong MinInternalValue = 0xF7FB_86F2_6FC0_FFFF; // -9_999_999_999_999_999 x 10^369
+
+ public static Decimal64 PositiveInfinity => new Decimal64(PositiveInfinityValue);
+ public static Decimal64 NegativeInfinity => new Decimal64(NegativeInfinityValue);
+ public static Decimal64 NaN => new Decimal64(QuietNaNValue);
+ public static Decimal64 NegativeZero => new Decimal64(NegativeZeroValue);
+ public static Decimal64 Zero => new Decimal64(ZeroValue);
+ public static Decimal64 MinValue => new Decimal64(MinInternalValue);
+ public static Decimal64 MaxValue => new Decimal64(MaxInternalValue);
+
+
+ private static ReadOnlySpan UInt64Powers10 =>
+ [
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+ 10000000000,
+ 100000000000,
+ 1000000000000,
+ 10000000000000,
+ 100000000000000,
+ 1000000000000000,
+ ];
+
+ public Decimal64(long significand, int exponent)
+ {
+ _value = Number.ConstructorToDecimalIeee754Bits(significand < 0, (ulong)Math.Abs(significand), exponent);
+ }
+
+ internal Decimal64(ulong value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Parses a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string. If the input exceeds Decimal64's range, a or is returned.
+ public static Decimal64 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ ///
+ /// Parses a from a in the given .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// The equivalent value representing the input string. If the input exceeds Decimal64's range, a or is returned.
+ public static Decimal64 Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
+
+ ///
+ public static Decimal64 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal64's range, a or is returned.
+ public static Decimal64 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ ///
+ /// Parses a from a and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal64's range, a or is returned.
+ public static Decimal64 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Parses a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string. If the input exceeds Decimal64's range, a or is returned.
+ public static Decimal64 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal64's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal64 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ /// Tries to parse a from a in the default parse style.
+ ///
+ /// The input to be parsed.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal64's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, out Decimal64 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+
+ ///
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given .
+ ///
+ /// The input to be parsed.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal64's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result) => TryParse(s, NumberStyles.Number, provider, out result);
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal64's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.TryParseDecimalIeee754(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ /// Tries to parse a from a with the given and .
+ ///
+ /// The input to be parsed.
+ /// The used to parse the input.
+ /// A format provider.
+ /// The equivalent value representing the input string if the parse was successful. If the input exceeds Decimal64's range, a or is returned. If the parse was unsuccessful, a default value is returned.
+ /// if the parse was successful, otherwise.
+ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+
+ if (s == null)
+ {
+ result = default;
+ return false;
+ }
+ return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ public int CompareTo(object? value)
+ {
+ if (value is not Decimal64 other)
+ {
+ return (value is null) ? 1 : throw new ArgumentException(SR.Arg_MustBeDecimal64);
+ }
+ return CompareTo(other);
+ }
+
+ ///
+ public int CompareTo(Decimal64 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value);
+ }
+
+ ///
+ public bool Equals(Decimal64 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value) == 0;
+ }
+
+ ///
+ /// Returns a value that indicates whether this instance is equal to a specified .
+ ///
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal64 && Equals((Decimal64)obj);
+ }
+
+ ///
+ /// Serves as the default hash function.
+ ///
+ public override int GetHashCode()
+ {
+ return _value.GetHashCode();
+ }
+
+ ///
+ /// Returns a string representation of the current value.
+ ///
+ public override string ToString()
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.CurrentInfo);
+ }
+
+ ///
+ /// Returns a string representation of the current value with the specified .
+ ///
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ /// Returns a string representation of the current value using the specified and .
+ ///
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimalIeee754(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.Precision => Precision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.BufferLength => Number.Decimal64NumberBufferLength;
+
+ static string IDecimalIeee754ParseAndFormatInfo.ToDecStr(ulong significand)
+ {
+ return Number.UInt64ToDecStr(significand);
+ }
+
+ Number.DecodedDecimalIeee754 IDecimalIeee754ParseAndFormatInfo.Unpack()
+ {
+ return Number.UnpackDecimalIeee754(_value);
+ }
+
+ static unsafe ulong IDecimalIeee754ParseAndFormatInfo.NumberToSignificand(ref Number.NumberBuffer number, int digits)
+ {
+ return Number.DigitsToUInt64(number.DigitsPtr, digits);
+ }
+
+ static Decimal64 IDecimalIeee754ParseAndFormatInfo.Construct(ulong value) => new Decimal64(value);
+
+ static int IDecimalIeee754ParseAndFormatInfo.ConvertToExponent(ulong value) => (int)value;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.Power10(int exponent) => UInt64Powers10[exponent];
+
+ static (ulong Quotient, ulong Remainder) IDecimalIeee754ParseAndFormatInfo.DivRemPow10(ulong value, int exponent)
+ {
+ ulong power = UInt64Powers10[exponent];
+ return Math.DivRem(value, power);
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.CountDigits(ulong significand) => FormattingHelpers.CountDigits(significand);
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 385;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinScale => -397;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxExponent => MaxExponent;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MinExponent => MinExponent;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.PositiveInfinity => PositiveInfinityValue;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.NegativeInfinity => NegativeInfinityValue;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.Zero => ZeroValue;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.NegativeZero => NegativeZeroValue;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.NaN => QuietNaNValue;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsEncoding => 64;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.SignMask => SignMask;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.G0G1Mask => G0G1Mask;
+
+ static int IDecimalIeee754ParseAndFormatInfo.ExponentBias => ExponentBias;
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberBitsSignificand => 50;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo.G0ToGwPlus1ExponentMask => 0x7FE0_0000_0000_0000;
+
+ static ulong IDecimalIeee754ParseAndFormatInfo