diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs
index 44c1d47d96dca..325f752ec625c 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 e6246a6f98107..f4f5c3d30e95c 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.
@@ -3203,6 +3212,15 @@
Value was either too large or too small for a Decimal.
+
+ Value was either too large or too small for a Decimal32.
+
+
+ Value was either too large or too small for a Decimal64.
+
+
+ Value was either too large or too small for a Decimal128.
+
The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue.
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 b8cb658322f89..67c9d3e3e8267 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
@@ -433,6 +433,10 @@
+
+
+
+
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 0000000000000..790773743e85c
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs
@@ -0,0 +1,255 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Numerics;
+
+namespace System
+{
+ internal interface IDecimalIeee754ConstructorInfo
+ where TSelf : unmanaged, IDecimalIeee754ConstructorInfo
+ where TSignificand : IBinaryInteger
+ where TValue : IBinaryInteger
+ {
+ static abstract TSignificand MaxSignificand { get; }
+ static abstract int MaxDecimalExponent { get; }
+ static abstract int MinDecimalExponent { get; }
+ static abstract int NumberDigitsPrecision { get; }
+ static abstract int Bias { get; }
+ static abstract int CountDigits(TSignificand number);
+ static abstract TSignificand Power10(int exponent);
+ static abstract int NumberBitsEncoding { get; }
+ static abstract TValue G0G1Mask { get; }
+ static abstract TValue MostSignificantBitOfSignificandMask { get; }
+ static abstract TValue SignMask { get; }
+ static abstract int NumberBitsCombinationField { get; }
+ static abstract int NumberBitsExponent { get; }
+ static abstract TValue PositiveInfinityBits { get; }
+ static abstract TValue NegativeInfinityBits { get; }
+ static abstract TValue Zero { get; }
+ static abstract string OverflowMessage { get; }
+ }
+
+ internal interface IDecimalIeee754UnpackInfo
+ where TSelf : unmanaged, IDecimalIeee754UnpackInfo
+ where TSignificand : IBinaryInteger
+ where TValue : IBinaryInteger
+ {
+ 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 int NumberBitsSignificand { get; }
+ static abstract int NumberDigitsPrecision { get; }
+ static abstract int Bias { get; }
+ static abstract TValue MostSignificantBitOfSignificandMask { get; }
+ static abstract int ConvertToExponent(TValue value);
+ static abstract TSignificand ConvertToSignificand(TValue value);
+ static abstract TSignificand Power10(int exponent);
+ }
+
+ internal static partial class Number
+ {
+ internal static TValue CalDecimalIeee754(TSignificand significand, int exponent)
+ where TDecimal : unmanaged, IDecimalIeee754ConstructorInfo
+ where TSignificand : IBinaryInteger
+ where TValue : IBinaryInteger
+ {
+ if (significand == TSignificand.Zero)
+ {
+ return TValue.Zero;
+ }
+
+ TSignificand unsignedSignificand = significand > TSignificand.Zero ? significand : -significand;
+
+ if ((unsignedSignificand > TDecimal.MaxSignificand && exponent >= TDecimal.MaxDecimalExponent)
+ || (unsignedSignificand == TDecimal.MaxSignificand && exponent > TDecimal.MaxDecimalExponent))
+ {
+ return significand > TSignificand.Zero ? TDecimal.PositiveInfinityBits : TDecimal.NegativeInfinityBits;
+ }
+
+ TSignificand ten = TSignificand.CreateTruncating(10);
+ if (exponent < TDecimal.MinDecimalExponent)
+ {
+ while (unsignedSignificand > TSignificand.Zero && exponent < TDecimal.MinDecimalExponent)
+ {
+ unsignedSignificand /= ten;
+ ++exponent;
+ }
+ if (unsignedSignificand == TSignificand.Zero)
+ {
+ return TDecimal.Zero;
+ }
+ }
+
+ if (unsignedSignificand > TDecimal.MaxSignificand)
+ {
+ int numberDigitsRemoving = TDecimal.CountDigits(unsignedSignificand) - TDecimal.NumberDigitsPrecision;
+
+ if (exponent + numberDigitsRemoving > TDecimal.MaxDecimalExponent)
+ {
+ throw new OverflowException(TDecimal.OverflowMessage);
+ }
+
+ exponent += numberDigitsRemoving;
+ TSignificand divisor = TDecimal.Power10(numberDigitsRemoving);
+ TSignificand quotient = unsignedSignificand / divisor;
+ TSignificand remainder = unsignedSignificand % divisor;
+ TSignificand midPoint = divisor >> 1;
+ bool needRounding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One);
+
+ if (needRounding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent)
+ {
+ unsignedSignificand = TDecimal.Power10(TDecimal.NumberDigitsPrecision - 1);
+ exponent++;
+ }
+ else if (needRounding && quotient < TDecimal.MaxSignificand)
+ {
+ unsignedSignificand = quotient + TSignificand.One;
+ }
+ else
+ {
+ unsignedSignificand = quotient;
+ }
+ }
+ else if (exponent > TDecimal.MaxDecimalExponent)
+ {
+ int numberZeroDigits = exponent - TDecimal.MaxDecimalExponent;
+ int numberSignificandDigits = TDecimal.CountDigits(unsignedSignificand);
+
+ if (numberSignificandDigits + numberZeroDigits > TDecimal.NumberDigitsPrecision)
+ {
+ throw new OverflowException(TDecimal.OverflowMessage);
+ }
+ unsignedSignificand *= TDecimal.Power10(numberZeroDigits);
+ exponent -= numberZeroDigits;
+ }
+
+ exponent += TDecimal.Bias;
+
+ TValue value = TValue.Zero;
+ TValue exponentVal = TValue.CreateTruncating(exponent);
+ TValue significandVal = TValue.CreateTruncating(unsignedSignificand);
+ bool msbSignificand = (significandVal & TDecimal.MostSignificantBitOfSignificandMask) != TValue.Zero;
+
+ if (significand < TSignificand.Zero)
+ {
+ value = TDecimal.SignMask;
+ }
+
+ if (msbSignificand)
+ {
+ value |= TDecimal.G0G1Mask;
+ exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 3;
+ value |= exponentVal;
+ significandVal ^= TDecimal.MostSignificantBitOfSignificandMask;
+ value |= significandVal;
+ }
+ else
+ {
+ exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 1;
+ value |= exponentVal;
+ value |= significandVal;
+ }
+
+ return value;
+ }
+
+ internal struct DecimalIeee754
+ where TSignificand : IBinaryInteger
+ {
+ public bool Signed { get; }
+ public int Exponent { get; }
+ public TSignificand Significand { get; }
+
+ public DecimalIeee754(bool signed, int exponent, TSignificand significand)
+ {
+ Signed = signed;
+ Exponent = exponent;
+ Significand = significand;
+ }
+ }
+
+ internal static DecimalIeee754 UnpackDecimalIeee754(TValue value)
+ where TDecimal : unmanaged, IDecimalIeee754UnpackInfo
+ where TSignificand : IBinaryInteger
+ where TValue : IBinaryInteger
+ {
+ bool signed = (value & TDecimal.SignMask) != TValue.Zero;
+ TSignificand significand;
+ int exponent;
+
+ if ((value & TDecimal.G0G1Mask) == TDecimal.G0G1Mask)
+ {
+ exponent = TDecimal.ConvertToExponent((value & TDecimal.G2ToGwPlus3ExponentMask) >> (TDecimal.NumberBitsSignificand + 1));
+ significand = TDecimal.ConvertToSignificand((value & TDecimal.GwPlus4SignificandMask) | TDecimal.MostSignificantBitOfSignificandMask);
+ }
+ else
+ {
+ exponent = TDecimal.ConvertToExponent((value & TDecimal.G0ToGwPlus1ExponentMask) >> (TDecimal.NumberBitsSignificand + 3));
+ significand = TDecimal.ConvertToSignificand(value & TDecimal.GwPlus2ToGwPlus4SignificandMask);
+ }
+
+ return new DecimalIeee754(signed, exponent - TDecimal.Bias, significand);
+ }
+
+ internal static int CompareDecimalIeee754(TValue currentValue, TValue otherValue)
+ where TDecimal : unmanaged, IDecimalIeee754UnpackInfo
+ where TSignificand : IBinaryInteger
+ where TValue : IBinaryInteger
+ {
+ if (currentValue == otherValue)
+ {
+ return 0;
+ }
+ DecimalIeee754 current = UnpackDecimalIeee754(currentValue);
+ DecimalIeee754 other = UnpackDecimalIeee754(otherValue);
+
+ if (current.Signed && !other.Signed) return -1;
+
+ if (!current.Signed && other.Signed) return 1;
+
+ if (current.Exponent > other.Exponent)
+ {
+ return current.Signed ? -InternalUnsignedCompare(current, other) : InternalUnsignedCompare(current, other);
+ }
+
+ if (current.Exponent < other.Exponent)
+ {
+ return current.Signed ? InternalUnsignedCompare(other, current) : -InternalUnsignedCompare(current, other);
+ }
+
+ if (current.Significand == other.Significand) return 0;
+
+ if (current.Significand > other.Significand)
+ {
+ return current.Signed ? -1 : 1;
+ }
+ else
+ {
+ return current.Signed ? 1 : -1;
+ }
+
+ static int InternalUnsignedCompare(DecimalIeee754 biggerExp, DecimalIeee754 smallerExp)
+ {
+ if (biggerExp.Significand >= smallerExp.Significand) return 1;
+
+ int diffExponent = biggerExp.Exponent - smallerExp.Exponent;
+ if (diffExponent < TDecimal.NumberDigitsPrecision)
+ {
+ TSignificand factor = TDecimal.Power10(diffExponent);
+ TSignificand quotient = smallerExp.Significand / biggerExp.Significand;
+ TSignificand remainder = smallerExp.Significand % biggerExp.Significand;
+
+ if (quotient < factor) return 1;
+ if (quotient > factor) return -1;
+ if (remainder > TSignificand.Zero) return -1;
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+ }
+}
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 38a752c6810ff..55510ffe1efcd 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
@@ -295,6 +295,84 @@ internal static partial class Number
"80818283848586878889"u8 +
"90919293949596979899"u8).ToArray();
+ public static unsafe string FormatDecimal32(Decimal32 value, ReadOnlySpan format, NumberFormatInfo info)
+ {
+ char fmt = ParseFormatSpecifier(format, out int digits);
+
+ byte* pDigits = stackalloc byte[Decimal32NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, Decimal32NumberBufferLength);
+
+ Decimal32ToNumber(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();
+ return result;
+ }
+
+ public static unsafe string FormatDecimal64(Decimal64 value, ReadOnlySpan format, NumberFormatInfo info)
+ {
+ char fmt = ParseFormatSpecifier(format, out int digits);
+
+ byte* pDigits = stackalloc byte[Decimal64NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, Decimal64NumberBufferLength);
+
+ Decimal64ToNumber(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();
+ return result;
+ }
+
+ public static unsafe string FormatDecimal128(Decimal128 value, ReadOnlySpan format, NumberFormatInfo info)
+ {
+ char fmt = ParseFormatSpecifier(format, out int digits);
+
+ byte* pDigits = stackalloc byte[Decimal128NumberBufferLength];
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, Decimal128NumberBufferLength);
+
+ Decimal128ToNumber(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();
+ return result;
+ }
+
public static unsafe string FormatDecimal(decimal value, ReadOnlySpan format, NumberFormatInfo info)
{
char fmt = ParseFormatSpecifier(format, out int digits);
@@ -348,6 +426,129 @@ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan decimal32 = UnpackDecimalIeee754(d._value);
+ number.IsNegative = decimal32.Signed;
+
+ byte* p = buffer + Decimal32Precision;
+ p = UInt32ToDecChars(p, (uint)decimal32.Significand, 0);
+ int numberDigitsSignificand = (int)((buffer + Decimal32Precision) - p);
+
+ byte* dst = number.DigitsPtr;
+ int i = numberDigitsSignificand;
+ while (--i >= 0)
+ {
+ *dst++ = *p++;
+ }
+
+ number.Scale = decimal32.Significand != 0 ? numberDigitsSignificand + decimal32.Exponent : 0;
+
+ if (decimal32.Exponent >= 0)
+ {
+ number.DigitsCount = numberDigitsSignificand + decimal32.Exponent;
+
+ if (decimal32.Exponent > 0)
+ {
+ i = decimal32.Exponent;
+ while (--i >= 0)
+ {
+ *dst++ = (byte)'0';
+ }
+ }
+ }
+ else
+ {
+ number.DigitsCount = numberDigitsSignificand;
+ }
+
+ *dst = (byte)'\0';
+
+ number.CheckConsistency();
+ }
+ internal static unsafe void Decimal64ToNumber(Decimal64 d, ref NumberBuffer number)
+ {
+ byte* buffer = number.DigitsPtr;
+ DecimalIeee754 decimal64 = UnpackDecimalIeee754(d._value);
+ number.IsNegative = decimal64.Signed;
+ byte* p = buffer + Decimal64Precision;
+
+ p = UInt64ToDecChars(p, (ulong)decimal64.Significand, 0);
+ int numberDigitsSignificand = (int)((buffer + Decimal64Precision) - p);
+
+ byte* dst = number.DigitsPtr;
+ int i = numberDigitsSignificand;
+ while (--i >= 0)
+ {
+ *dst++ = *p++;
+ }
+
+ number.Scale = decimal64.Significand != 0 ? numberDigitsSignificand + decimal64.Exponent : 0;
+
+ if (decimal64.Exponent >= 0)
+ {
+ number.DigitsCount = numberDigitsSignificand + decimal64.Exponent;
+
+ if (decimal64.Exponent > 0)
+ {
+ i = decimal64.Exponent;
+ while (--i >= 0)
+ {
+ *dst++ = (byte)'0';
+ }
+ }
+ }
+ else
+ {
+ number.DigitsCount = numberDigitsSignificand;
+ }
+
+ *dst = (byte)'\0';
+
+ number.CheckConsistency();
+ }
+ internal static unsafe void Decimal128ToNumber(Decimal128 d, ref NumberBuffer number)
+ {
+ byte* buffer = number.DigitsPtr;
+ DecimalIeee754 decimal128 = UnpackDecimalIeee754(new UInt128(d._upper, d._lower));
+ number.IsNegative = decimal128.Signed;
+ byte* p = buffer + Decimal128Precision;
+
+ p = UInt128ToDecChars(p, (UInt128)decimal128.Significand, 0);
+ int numberDigitsSignificand = (int)((buffer + Decimal128Precision) - p);
+
+ byte* dst = number.DigitsPtr;
+ int i = numberDigitsSignificand;
+ while (--i >= 0)
+ {
+ *dst++ = *p++;
+ }
+
+ number.Scale = decimal128.Significand != 0 ? numberDigitsSignificand + decimal128.Exponent : 0;
+
+ if (decimal128.Exponent >= 0)
+ {
+ number.DigitsCount = numberDigitsSignificand + decimal128.Exponent;
+
+ if (decimal128.Exponent > 0)
+ {
+ i = decimal128.Exponent;
+ while (--i >= 0)
+ {
+ *dst++ = (byte)'0';
+ }
+ }
+ }
+ else
+ {
+ number.DigitsCount = numberDigitsSignificand;
+ }
+
+ *dst = (byte)'\0';
+
+ number.CheckConsistency();
+ }
internal static unsafe void DecimalToNumber(scoped ref decimal d, ref NumberBuffer number)
{
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 bb80ebd3ff67a..ea1a1d6898862 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
@@ -99,6 +99,22 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI
static abstract int MaxPrecisionCustomFormat { get; }
}
+ internal interface IDecimalIeee754ParseAndFormatInfo
+ where TSelf : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ {
+ static abstract int NumberDigitsPrecision { get; }
+ static abstract int MaxScale { get; }
+ }
+
+ internal interface IDecimalIeee754TryParseInfo
+ where TSelf : unmanaged, IDecimalIeee754TryParseInfo
+ where TSignificand : unmanaged, IBinaryInteger
+ {
+ static abstract int DecimalNumberBufferLength { get; }
+ static abstract bool TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out TSignificand significand, out int exponent);
+ static abstract TSelf Construct(TSignificand significand, int exponent);
+ }
+
internal static partial class Number
{
private const int Int32Precision = 10;
@@ -107,6 +123,9 @@ internal static partial class Number
private const int UInt64Precision = 20;
private const int Int128Precision = 39;
private const int UInt128Precision = 39;
+ private const int Decimal32Precision = 7;
+ private const int Decimal64Precision = 16;
+ private const int Decimal128Precision = 34;
private const int FloatingPointMaxExponent = 309;
private const int FloatingPointMinExponent = -324;
@@ -733,6 +752,54 @@ internal static decimal ParseDecimal(ReadOnlySpan value, NumberSty
return result;
}
+ internal static Decimal32 ParseDecimal32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info)
+ where TChar : unmanaged, IUtfChar
+ {
+ ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal32 result);
+ if (status != ParsingStatus.OK)
+ {
+ if (status == ParsingStatus.Failed)
+ {
+ ThrowFormatException(value);
+ }
+ ThrowOverflowException(SR.Overflow_Decimal32);
+ }
+
+ return result;
+ }
+
+ internal static Decimal64 ParseDecimal64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info)
+ where TChar : unmanaged, IUtfChar
+ {
+ ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal64 result);
+ if (status != ParsingStatus.OK)
+ {
+ if (status == ParsingStatus.Failed)
+ {
+ ThrowFormatException(value);
+ }
+ ThrowOverflowException(SR.Overflow_Decimal64);
+ }
+
+ return result;
+ }
+
+ internal static Decimal128 ParseDecimal128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info)
+ where TChar : unmanaged, IUtfChar
+ {
+ ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal128 result);
+ if (status != ParsingStatus.OK)
+ {
+ if (status == ParsingStatus.Failed)
+ {
+ ThrowFormatException(value);
+ }
+ ThrowOverflowException(SR.Overflow_Decimal128);
+ }
+
+ return result;
+ }
+
internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value)
{
number.CheckConsistency();
@@ -853,6 +920,70 @@ internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref deci
return true;
}
+ internal static unsafe bool TryNumberToDecimalIeee754(ref NumberBuffer number, out TSignificand significand, out int exponent)
+ where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo
+ where TSignificand : unmanaged, IBinaryInteger
+ {
+ number.CheckConsistency();
+
+ byte* p = number.DigitsPtr;
+ int c = *p;
+ significand = TSignificand.Zero;
+ exponent = 0;
+
+ if (c == 0)
+ {
+ return true;
+ }
+
+ if (number.Scale > TDecimal.MaxScale)
+ {
+ return false;
+ }
+
+ int digitIndex = 0;
+
+ while (digitIndex < TDecimal.NumberDigitsPrecision && c != 0)
+ {
+ digitIndex++;
+ significand *= TSignificand.CreateTruncating(10);
+ significand += TSignificand.CreateTruncating(c - '0');
+ c = *++p;
+ }
+
+ exponent = number.Scale - digitIndex;
+
+ if (digitIndex < number.DigitsCount)
+ {
+ if (c == '5')
+ {
+ int lastDigitSignificand = *(p - 1);
+ c = *++p;
+ bool tiedToEvenRounding = true;
+ while (digitIndex < number.DigitsCount && c != 0)
+ {
+ if (c != '0')
+ {
+ significand += TSignificand.One;
+ tiedToEvenRounding = false;
+ break;
+ }
+ c = *++p;
+ }
+ if (tiedToEvenRounding && lastDigitSignificand % 2 == 1)
+ {
+ significand += TSignificand.One;
+ }
+ }
+ else if (c > '5')
+ {
+ significand += TSignificand.One;
+ }
+ }
+
+ return true;
+ }
+
internal static TFloat ParseFloat(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info)
where TChar : unmanaged, IUtfChar
where TFloat : unmanaged, IBinaryFloatParseAndFormatInfo
@@ -884,6 +1015,29 @@ 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, IDecimalIeee754TryParseInfo
+ where TSignificand : unmanaged, IBinaryInteger
+ {
+ NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[TDecimal.DecimalNumberBufferLength]);
+ result = default;
+
+ if (!TryStringToNumber(value, styles, ref number, info))
+ {
+ return ParsingStatus.Failed;
+ }
+
+ if (!TDecimal.TryNumberToDecimalIeee754(ref number, out TSignificand significand, out int exponent))
+ {
+ return ParsingStatus.Overflow;
+ }
+
+ result = TDecimal.Construct(number.IsNegative ? -significand : significand, exponent);
+
+ return ParsingStatus.OK;
+ }
+
internal static bool SpanStartsWith(ReadOnlySpan span, TChar c)
where TChar : unmanaged, IUtfChar
{
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 0000000000000..3c487c8291205
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs
@@ -0,0 +1,268 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Buffers.Text;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Text;
+
+namespace System.Numerics
+{
+ public readonly struct Decimal128
+ : IComparable,
+ IComparable,
+ IEquatable,
+ ISpanParsable,
+ IDecimalIeee754ParseAndFormatInfo,
+ IDecimalIeee754ConstructorInfo,
+ IDecimalIeee754UnpackInfo,
+ IDecimalIeee754TryParseInfo
+ {
+#if BIGENDIAN
+ internal readonly ulong _upper;
+ internal readonly ulong _lower;
+#else
+ internal readonly ulong _lower;
+ internal readonly ulong _upper;
+#endif
+
+ private const int MaxDecimalExponent = 6111;
+ private const int MinDecimalExponent = -6176;
+ private const int NumberDigitsPrecision = 34;
+ private const int Bias = 6176;
+ private const int NumberBitsExponent = 14;
+ private static readonly UInt128 PositiveInfinityValue = new UInt128(upper: 0x7800000000000000, lower: 0);
+ private static readonly UInt128 NegativeInfinityValue = new UInt128(upper: 0xf800000000000000, lower: 0);
+ private static readonly UInt128 ZeroValue = new UInt128(0, 0);
+ private static readonly Int128 MaxSignificand = new Int128(upper: 542101086242752, lower: 4003012203950112767); // 9_999_999_999_999_999_999_999_999_999_999_999;
+ private static readonly UInt128 SignMask = new UInt128(0x8000_0000_0000_0000, 0);
+ private static readonly UInt128 G0G1Mask = new UInt128(0x6000_0000_0000_0000, 0);
+ private static readonly UInt128 MostSignificantBitOfSignificandMask = new UInt128(0x0002_0000_0000_0000, 0);
+
+ public Decimal128(Int128 significand, int exponent)
+ {
+ UInt128 value = Number.CalDecimalIeee754(significand, exponent);
+ _lower = value.Lower;
+ _upper = value.Upper;
+ }
+ public static Decimal128 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ 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);
+
+ public static Decimal128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ public static Decimal128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimal128(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+ public static Decimal128 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal128 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+ 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);
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result) => TryParse(s, NumberStyles.Number, provider, out result);
+ 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;
+ }
+ 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;
+ }
+
+ // 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 Decimal128, this method throws an ArgumentException.
+ //
+ public int CompareTo(object? value)
+ {
+ if (value == null)
+ {
+ return 1;
+ }
+
+ if (value is not Decimal128 i)
+ {
+ throw new ArgumentException(SR.Arg_MustBeDecimal128);
+ }
+
+ var current = new UInt128(_upper, _lower);
+ var other = new UInt128(i._upper, i._lower);
+
+ return Number.CompareDecimalIeee754(current, 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;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal128 && Equals((Decimal128)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return new UInt128(_upper, _lower).GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Number.FormatDecimal128(this, null, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimal128(this, format, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimal128(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimal128(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 6145;
+
+ static int IDecimalIeee754ConstructorInfo.CountDigits(Int128 number) => FormattingHelpers.CountDigits((UInt128)number);
+
+ static Int128 IDecimalIeee754ConstructorInfo.Power10(int exponent) => Int128Powers10[exponent];
+
+ static Int128 IDecimalIeee754ConstructorInfo.MaxSignificand => MaxSignificand;
+
+ static int IDecimalIeee754ConstructorInfo.MaxDecimalExponent => MaxDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.MinDecimalExponent => MinDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ConstructorInfo.Bias => Bias;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 128;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 17;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.PositiveInfinityBits => PositiveInfinityValue;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.NegativeInfinityBits => NegativeInfinityValue;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.Zero => ZeroValue;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static UInt128 IDecimalIeee754ConstructorInfo.SignMask => SignMask;
+
+ static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal128;
+
+ static int IDecimalIeee754UnpackInfo.ConvertToExponent(UInt128 value) => (int)value;
+
+ static Int128 IDecimalIeee754UnpackInfo.ConvertToSignificand(UInt128 value) => (Int128)value;
+
+ static Int128 IDecimalIeee754UnpackInfo.Power10(int exponent) => Int128Powers10[exponent];
+
+ static UInt128 IDecimalIeee754UnpackInfo.SignMask => SignMask;
+
+ static int IDecimalIeee754UnpackInfo.Bias => Bias;
+
+ static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static UInt128 IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask;
+
+ static UInt128 IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => new UInt128(0x7FFE_0000_0000_0000, 0);
+
+ static UInt128 IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => new UInt128(0x1FFF_8000_0000_0000, 0);
+
+ static UInt128 IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => new UInt128(0x0001_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ static UInt128 IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => new UInt128(0x0000_7FFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ static UInt128 IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 110;
+
+ private static Int128[] Int128Powers10 =>
+ [
+ new Int128(0, 1),
+ new Int128(0, 10),
+ new Int128(0, 100),
+ new Int128(0, 1000),
+ new Int128(0, 10000),
+ new Int128(0, 100000),
+ new Int128(0, 1000000),
+ new Int128(0, 10000000),
+ new Int128(0, 100000000),
+ new Int128(0, 1000000000),
+ new Int128(0, 10000000000),
+ new Int128(0, 100000000000),
+ new Int128(0, 1000000000000),
+ new Int128(0, 10000000000000),
+ new Int128(0, 100000000000000),
+ new Int128(0, 1000000000000000),
+ new Int128(0, 10000000000000000),
+ new Int128(0, 100000000000000000),
+ new Int128(0, 1000000000000000000),
+ new Int128(0, 10000000000000000000),
+ new Int128(5, 7766279631452241920),
+ new Int128(54, 3875820019684212736),
+ new Int128(542, 1864712049423024128),
+ new Int128(5421, 200376420520689664),
+ new Int128(54210, 2003764205206896640),
+ new Int128(542101, 1590897978359414784),
+ new Int128(5421010, 15908979783594147840),
+ new Int128(54210108, 11515845246265065472),
+ new Int128(542101086, 4477988020393345024),
+ new Int128(5421010862, 7886392056514347008),
+ new Int128(54210108624, 5076944270305263616),
+ new Int128(542101086242, 13875954555633532928),
+ new Int128(5421010862427, 9632337040368467968),
+ new Int128(54210108624275, 4089650035136921600),
+ new Int128(542101086242752, 4003012203950112768),
+ ];
+
+ static bool IDecimalIeee754TryParseInfo.TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out Int128 significand, out int exponent)
+ => Number.TryNumberToDecimalIeee754(ref number, out significand, out exponent);
+
+ static Decimal128 IDecimalIeee754TryParseInfo.Construct(Int128 significand, int exponent) => new Decimal128(significand, exponent);
+
+ static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal128NumberBufferLength;
+ }
+}
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 0000000000000..551c61c3e85ea
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs
@@ -0,0 +1,227 @@
+// 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,
+ IDecimalIeee754ParseAndFormatInfo,
+ IDecimalIeee754ConstructorInfo,
+ IDecimalIeee754UnpackInfo,
+ IDecimalIeee754TryParseInfo
+ {
+ internal readonly uint _value;
+
+ private const int MaxDecimalExponent = 90;
+ private const int MinDecimalExponent = -101;
+ private const int NumberDigitsPrecision = 7;
+ private const int Bias = 101;
+ private const int NumberBitsExponent = 8;
+ private const uint PositiveInfinityValue = 0x7800_0000;
+ private const uint NegativeInfinityValue = 0xF800_0000;
+ private const uint ZeroValue = 0x00000000;
+ private const int MaxSignificand = 9_999_999;
+ private const uint G0G1Mask = 0x6000_0000;
+ private const uint SignMask = 0x8000_0000;
+ private const uint MostSignificantBitOfSignificandMask = 0x0080_0000;
+
+ public Decimal32(int significand, int exponent)
+ {
+ _value = Number.CalDecimalIeee754(significand, exponent);
+ }
+
+ public static Decimal32 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ 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);
+
+ public static Decimal32 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ public static Decimal32 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimal32(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal32 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+ 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);
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result) => TryParse(s, NumberStyles.Number, provider, out result);
+ 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;
+ }
+ 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;
+ }
+
+ private static ReadOnlySpan Int32Powers10 =>
+ [
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ ];
+
+ // Compares this object to another object, returning an integer that
+ // indicates the relationship.
+ // Returns :
+ // 0 if the values are equal
+ // Negative number if _value is less than value
+ // Positive number if _value is more than value
+ // null is considered to be less than any instance, hence returns positive number
+ // If object is not of type Decimal32, this method throws an ArgumentException.
+ //
+ 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;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal32 && Equals((Decimal32)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return _value.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Number.FormatDecimal32(this, null, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimal32(this, format, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimal32(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimal32(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 97;
+
+ static int IDecimalIeee754ConstructorInfo.CountDigits(int number) => FormattingHelpers.CountDigits((uint)number);
+ static int IDecimalIeee754ConstructorInfo.Power10(int exponent) => Int32Powers10[exponent];
+
+ static int IDecimalIeee754ConstructorInfo.MaxSignificand => MaxSignificand;
+
+ static int IDecimalIeee754ConstructorInfo.MaxDecimalExponent => MaxDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.MinDecimalExponent => MinDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ConstructorInfo.Bias => Bias;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 32;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 11;
+
+ static uint IDecimalIeee754ConstructorInfo.PositiveInfinityBits => PositiveInfinityValue;
+
+ static uint IDecimalIeee754ConstructorInfo.NegativeInfinityBits => NegativeInfinityValue;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static uint IDecimalIeee754ConstructorInfo.Zero => ZeroValue;
+
+ static uint IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask;
+
+ static uint IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static uint IDecimalIeee754ConstructorInfo.SignMask => SignMask;
+
+ static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal32;
+
+ static uint IDecimalIeee754UnpackInfo.SignMask => SignMask;
+
+ static int IDecimalIeee754UnpackInfo.Bias => Bias;
+
+ static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754UnpackInfo.ConvertToExponent(uint value) => (int)value;
+
+ static int IDecimalIeee754UnpackInfo.ConvertToSignificand(uint value) => (int)value;
+
+ static int IDecimalIeee754UnpackInfo.Power10(int exponent) => Int32Powers10[exponent];
+
+ static uint IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask;
+
+ static uint IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7F80_0000;
+
+ static uint IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => 0x1FE0_0000;
+
+ static uint IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => 0x007F_FFFF;
+
+ static uint IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => 0x001F_FFFF;
+
+ static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 20;
+
+ static uint IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal32NumberBufferLength;
+
+ static bool IDecimalIeee754TryParseInfo.TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out int significand, out int exponent)
+ => Number.TryNumberToDecimalIeee754(ref number, out significand, out exponent);
+
+ static Decimal32 IDecimalIeee754TryParseInfo.Construct(int significand, int exponent) => new Decimal32(significand, exponent);
+ }
+}
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 0000000000000..79e7560e39272
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs
@@ -0,0 +1,237 @@
+// 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,
+ IDecimalIeee754ParseAndFormatInfo,
+ IDecimalIeee754ConstructorInfo,
+ IDecimalIeee754UnpackInfo,
+ IDecimalIeee754TryParseInfo
+ {
+ internal readonly ulong _value;
+
+ private const int MaxDecimalExponent = 369;
+ private const int MinDecimalExponent = -398;
+ private const int NumberDigitsPrecision = 16;
+ private const int Bias = 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 long MaxSignificand = 9_999_999_999_999_999;
+ private const ulong G0G1Mask = 0x6000_0000_0000_0000;
+ private const ulong SignMask = 0x8000_0000_0000_0000;
+ private const ulong MostSignificantBitOfSignificandMask = 0x0020_0000_0000_0000;
+
+ public Decimal64(long significand, int exponent)
+ {
+ _value = Number.CalDecimalIeee754(significand, exponent);
+ }
+
+ internal Decimal64(ulong value)
+ {
+ _value = value;
+ }
+
+ public static Decimal64 Parse(string s) => Parse(s, NumberStyles.Number, provider: null);
+
+ 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);
+
+ public static Decimal64 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
+
+ public static Decimal64 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
+ return Number.ParseDecimal64(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+ private static ReadOnlySpan Int64Powers10 =>
+ [
+ 1,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+ 10000000000,
+ 100000000000,
+ 1000000000000,
+ 10000000000000,
+ 100000000000000,
+ 1000000000000000,
+ ];
+
+ public static Decimal64 Parse(string s, NumberStyles style, IFormatProvider? provider)
+ {
+ if (s is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
+ }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ public static bool TryParse([NotNullWhen(true)] string? s, out Decimal64 result) => TryParse(s, NumberStyles.Number, provider: null, out result);
+ 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);
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result) => TryParse(s, NumberStyles.Number, provider, out result);
+ 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;
+ }
+ 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;
+ }
+
+ // 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 Decimal64, this method throws an ArgumentException.
+ //
+ public int CompareTo(object? value)
+ {
+ if (value == null)
+ {
+ return 1;
+ }
+
+ if (value is not Decimal64 i)
+ {
+ throw new ArgumentException(SR.Arg_MustBeDecimal64);
+ }
+
+ return Number.CompareDecimalIeee754(_value, i._value);
+ }
+
+ public int CompareTo(Decimal64 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value);
+ }
+
+ public bool Equals(Decimal64 other)
+ {
+ return Number.CompareDecimalIeee754(_value, other._value) == 0;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is Decimal64 && Equals((Decimal64)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return _value.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Number.FormatDecimal64(this, null, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+ {
+ return Number.FormatDecimal64(this, format, NumberFormatInfo.CurrentInfo);
+ }
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatDecimal64(this, null, NumberFormatInfo.GetInstance(provider));
+ }
+ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+ {
+ return Number.FormatDecimal64(this, format, NumberFormatInfo.GetInstance(provider));
+ }
+
+ static int IDecimalIeee754ParseAndFormatInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ParseAndFormatInfo.MaxScale => 385;
+
+ static int IDecimalIeee754ConstructorInfo.CountDigits(long number) => FormattingHelpers.CountDigits((ulong)number);
+
+ static long IDecimalIeee754ConstructorInfo.Power10(int exponent) => Int64Powers10[exponent];
+
+ static long IDecimalIeee754ConstructorInfo.MaxSignificand => MaxSignificand;
+
+ static int IDecimalIeee754ConstructorInfo.MaxDecimalExponent => MaxDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.MinDecimalExponent => MinDecimalExponent;
+
+ static int IDecimalIeee754ConstructorInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static int IDecimalIeee754ConstructorInfo.Bias => Bias;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 64;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 13;
+
+ static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent;
+
+ static ulong IDecimalIeee754ConstructorInfo.PositiveInfinityBits => PositiveInfinityValue;
+
+ static ulong IDecimalIeee754ConstructorInfo.NegativeInfinityBits => NegativeInfinityValue;
+
+ static ulong IDecimalIeee754ConstructorInfo.Zero => ZeroValue;
+
+ static ulong IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask;
+
+ static ulong IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static ulong IDecimalIeee754ConstructorInfo.SignMask => SignMask;
+
+ static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal64;
+
+ static int IDecimalIeee754UnpackInfo.ConvertToExponent(ulong value) => (int)value;
+
+ static long IDecimalIeee754UnpackInfo.ConvertToSignificand(ulong value) => (long)value;
+
+ static long IDecimalIeee754UnpackInfo.Power10(int exponent) => Int64Powers10[exponent];
+
+ static ulong IDecimalIeee754UnpackInfo.SignMask => SignMask;
+
+ static int IDecimalIeee754UnpackInfo.Bias => Bias;
+
+ static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision;
+
+ static ulong IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask;
+
+ static ulong IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7FE0_0000_0000_0000;
+
+ static ulong IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => 0x1FF8_0000_0000_0000;
+
+ static ulong IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => 0x001F_FFFF_FFFF_FFFF;
+
+ static ulong IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => 0x0007_FFFF_FFFF_FFFF;
+
+ static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 50;
+
+ static ulong IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask;
+
+ static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal64NumberBufferLength;
+
+ static bool IDecimalIeee754TryParseInfo.TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out long significand, out int exponent)
+ => Number.TryNumberToDecimalIeee754(ref number, out significand, out exponent);
+
+ static Decimal64 IDecimalIeee754TryParseInfo.Construct(long significand, int exponent) => new Decimal64(significand, exponent);
+ }
+}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index a08103e2de4a0..cbba9b7d037d8 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -11096,6 +11096,103 @@ public static void HtmlEncode(string? value, System.IO.TextWriter output) { }
}
namespace System.Numerics
{
+ public readonly struct Decimal32
+ : System.IComparable,
+ System.IComparable,
+ System.IEquatable,
+ System.ISpanParsable
+ {
+ public Decimal32(int significand, int exponent) { throw null; }
+
+ public int CompareTo(object? value) { throw null; }
+ public int CompareTo(Decimal32 other) { throw null; }
+ public bool Equals(Decimal32 other) { throw null; }
+
+ public static Decimal32 Parse(string s) { throw null; }
+ public static Decimal32 Parse(string s, System.Globalization.NumberStyles style) { throw null; }
+ public static Decimal32 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; }
+ public static Decimal32 Parse(string s, IFormatProvider? provider) { throw null; }
+ public static Decimal32 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Number, IFormatProvider? provider = null) { throw null; }
+ public static Decimal32 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; }
+
+ public override string ToString() { throw null; }
+ public string ToString(System.IFormatProvider? provider) { throw null; }
+ public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format) { throw null; }
+ public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format, System.IFormatProvider? provider) { throw null; }
+
+ public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal32 result) { throw null; }
+ public static bool TryParse(ReadOnlySpan s, out Decimal32 result) { throw null; }
+ public static bool TryParse(ReadOnlySpan