From ac5e4473d2b44f1922b47da30611c14c09c937d6 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Mon, 11 Mar 2024 13:08:12 +0700 Subject: [PATCH 01/23] add constructor info --- .../src/System/Number.DecimalIeee754.cs | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs 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..503a6e8e07cd2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -0,0 +1,97 @@ +// 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.Numerics; +using System.Text; + +namespace System +{ + internal interface IDecimalIeee754ConstructorInfo + where TSelf : unmanaged, IDecimalIeee754ConstructorInfo + where TSignificand : IBinaryInteger + { + static abstract TSignificand MaxSignificand { get; } + static abstract int MaxDecimalExponent { get; } + static abstract int MinDecimalExponent { get; } + static abstract int NumberDigitsPrecision { get; } + static abstract int CountDigits(TSignificand number); + static abstract TSignificand Power10(int exponent); + } + + internal static partial class Number + { + internal static bool ParseDecimalIeee754(ref TSignificand significand, ref int exponent) + where TDecimal : unmanaged, IDecimalIeee754ConstructorInfo + where TSignificand : IBinaryInteger + { + TSignificand unsignedSignificand = significand > TSignificand.Zero ? significand : -significand; + + if (unsignedSignificand > TDecimal.MaxSignificand && exponent > TDecimal.MaxDecimalExponent) + { + throw new OverflowException(SR.Overflow_Decimal); + } + + TSignificand ten = TSignificand.CreateTruncating(10); + if (exponent < TDecimal.MinDecimalExponent) + { + while (unsignedSignificand % ten == TSignificand.Zero) + { + unsignedSignificand /= ten; + ++exponent; + } + if (exponent < TDecimal.MinDecimalExponent) + { + throw new OverflowException(SR.Overflow_Decimal); + } + } + + if (unsignedSignificand > TDecimal.MaxSignificand) + { + int numberDigitsRemoving = TDecimal.CountDigits(unsignedSignificand) - TDecimal.NumberDigitsPrecision; + + if (exponent + numberDigitsRemoving > TDecimal.MaxDecimalExponent) + { + throw new OverflowException(SR.Overflow_Decimal); + } + + exponent += numberDigitsRemoving; + TSignificand two = TSignificand.CreateTruncating(2); + TSignificand divisor = TDecimal.Power10(numberDigitsRemoving); + TSignificand quotient = unsignedSignificand / divisor; + TSignificand remainder = unsignedSignificand % divisor; + TSignificand midPoint = divisor / two; + bool needRouding = remainder > midPoint || (remainder == midPoint && quotient % two == TSignificand.One); + + if (needRouding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent) + { + unsignedSignificand = TDecimal.Power10(TDecimal.NumberDigitsPrecision - 1); + exponent++; + } + else if (needRouding && 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(SR.Overflow_Decimal); + } + unsignedSignificand *= TDecimal.Power10(numberZeroDigits); + } + significand = unsignedSignificand; + return true; + } + } +} From 1af33374e92a1c5041edae4f579f9bf79840da6c Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Sat, 30 Mar 2024 21:55:12 +0700 Subject: [PATCH 02/23] Add Decimal32 --- .../src/Resources/Strings.resx | 9 + .../src/System/Number.DecimalIeee754.cs | 119 +++++++++- .../src/System/Number.Parsing.cs | 103 +++++++++ .../src/System/Numerics/Decimal32.cs | 205 ++++++++++++++++++ .../src/System/Numerics/Decimal64.cs | 55 +++++ 5 files changed, 479 insertions(+), 12 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index b7a2c2aa9d866..186c4dd740530 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -525,6 +525,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/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 503a6e8e07cd2..5ecaf801e3da8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -1,43 +1,69 @@ // 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.Numerics; -using System.Text; +using static System.Number; namespace System { - internal interface IDecimalIeee754ConstructorInfo - where TSelf : unmanaged, IDecimalIeee754ConstructorInfo + 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 MostSignificantBitNumberOfSignificand { get; } + static abstract int NumberBitsEncoding { 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; } + } + + internal interface IDecimalIeee754UnpackInfo + where TSelf : unmanaged, IDecimalIeee754UnpackInfo + where TSignificand : IBinaryInteger + where TValue : IBinaryInteger + { + static abstract TValue SignMask { get; } + static abstract int NumberBitsEncoding { get; } + static abstract int NumberBitsExponent { get; } + static abstract int Bias { get; } + static abstract TSignificand TwoPowerMostSignificantBitNumberOfSignificand { get; } + static abstract int ConvertToExponent(TValue value); + static abstract TSignificand ConvertToSignificand(TValue value); } internal static partial class Number { - internal static bool ParseDecimalIeee754(ref TSignificand significand, ref int exponent) - where TDecimal : unmanaged, IDecimalIeee754ConstructorInfo + 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) { - throw new OverflowException(SR.Overflow_Decimal); + return significand > TSignificand.Zero ? TDecimal.PositiveInfinityBits : TDecimal.NegativeInfinityBits; } TSignificand ten = TSignificand.CreateTruncating(10); if (exponent < TDecimal.MinDecimalExponent) { - while (unsignedSignificand % ten == TSignificand.Zero) + while (unsignedSignificand >= ten) { unsignedSignificand /= ten; ++exponent; @@ -90,8 +116,77 @@ internal static bool ParseDecimalIeee754(ref TSignifican } unsignedSignificand *= TDecimal.Power10(numberZeroDigits); } - significand = unsignedSignificand; - return true; + + exponent += TDecimal.Bias; + bool msbSignificand = (unsignedSignificand & TSignificand.One << TDecimal.MostSignificantBitNumberOfSignificand) != TSignificand.Zero; + + TValue value = TValue.Zero; + TValue exponentVal = TValue.CreateTruncating(exponent); + TValue significandVal = TValue.CreateTruncating(unsignedSignificand); + + if (significand < TSignificand.Zero) + { + value = TValue.One << TDecimal.NumberBitsEncoding - 1; + } + + if (msbSignificand) + { + value ^= TValue.One << TDecimal.NumberBitsEncoding - 2; + value ^= TValue.One << TDecimal.NumberBitsEncoding - 3; + exponentVal <<= TDecimal.NumberBitsEncoding - 4; + value ^= exponentVal; + significandVal <<= TDecimal.NumberBitsEncoding - TDecimal.MostSignificantBitNumberOfSignificand; + significandVal >>= TDecimal.NumberBitsCombinationField; + 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; + TValue g0g1Bits = (value << 1) >> TDecimal.NumberBitsEncoding - 2; + TSignificand significand; + int exponent; + + if (g0g1Bits == TValue.CreateTruncating(3)) + { + exponent = TDecimal.ConvertToExponent((value << 3) >> TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent); + significand = TDecimal.ConvertToSignificand((value << TDecimal.NumberBitsEncoding + 3) >> TDecimal.NumberBitsEncoding + 3); + significand += TDecimal.TwoPowerMostSignificantBitNumberOfSignificand; + } + else + { + exponent = TDecimal.ConvertToExponent((value << 1) >> TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent); + significand = TDecimal.ConvertToSignificand((value << TDecimal.NumberBitsExponent + 1) >> TDecimal.NumberBitsExponent + 1); + } + + return new DecimalIeee754(signed, exponent - TDecimal.Bias, significand); } } } 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 72b423f265e78..bfe2edc07aaaf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -87,6 +87,12 @@ internal interface IBinaryFloatParseAndFormatInfo : IBinaryFloatingPointI static abstract ulong FloatToBits(TSelf value); } + internal interface IDecimalIeee754ParseAndFormatInfo + where TSelf : unmanaged, IDecimalIeee754ParseAndFormatInfo + { + static abstract int NumberDigitsPrecision { get; } + } + internal static partial class Number { private const int Int32Precision = 10; @@ -981,6 +987,22 @@ 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 = TryParseDecimal32(value, styles, info, out Decimal32 result); + if (status != ParsingStatus.OK) + { + if (status == ParsingStatus.Failed) + { + ThrowFormatException(value); + } + ThrowOverflowException(SR.Overflow_Decimal); + } + + return result; + } + internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value) { number.CheckConsistency(); @@ -1101,6 +1123,65 @@ 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.GetDigitsPointer(); + int c = *p; + significand = TSignificand.Zero; + exponent = 0; + + if (c == 0) + { + return true; + } + + 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 @@ -1132,6 +1213,28 @@ internal static ParsingStatus TryParseDecimal(ReadOnlySpan value, return ParsingStatus.OK; } + internal static ParsingStatus TryParseDecimal32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal32 result) + where TChar : unmanaged, IUtfChar + { + NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[DecimalNumberBufferLength]); + + result = new Decimal32(0, 0); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberToDecimalIeee754(ref number, out int significand, out int exponent)) + { + return ParsingStatus.Overflow; + } + + result = new Decimal32(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/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs new file mode 100644 index 0000000000000..10cf04cb5af39 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -0,0 +1,205 @@ +// 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, + IEquatable, + IDecimalIeee754ParseAndFormatInfo, + IDecimalIeee754ConstructorInfo, + IDecimalIeee754UnpackInfo + { + 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 = 0x78000000; + private const uint NegativeInfinityValue = 0xf8000000; + private const uint ZeroValue = 0x00000000; + private const int MaxSignificand = 9_999_999; + private static readonly Decimal32 PositiveInfinity = new Decimal32(PositiveInfinityValue); + private static readonly Decimal32 NegativeInfinity = new Decimal32(NegativeInfinityValue); + + public Decimal32(int significand, int exponent) + { + _value = Number.CalDecimalIeee754(significand, exponent); + } + + internal Decimal32(uint value) + { + _value = value; + } + + 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); + } + + private static ReadOnlySpan Int32Powers10 => + [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + ]; + + public int CompareTo(object? value) + { + if (value == null) + { + return 1; + } + + if (value is not Decimal32 i) + { + throw new ArgumentException(SR.Arg_MustBeDecimal32); + } + + return CoreCompareTo(i); + } + + private int CoreCompareTo(Decimal32 obj) + { + if (obj._value == _value) + { + return 0; + } + + Number.DecimalIeee754 current = Number.UnpackDecimalIeee754(_value); + Number.DecimalIeee754 other = Number.UnpackDecimalIeee754(obj._value); + + 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(Number.DecimalIeee754 biggerExp, Number.DecimalIeee754 smallerExp) + { + if (biggerExp.Significand >= smallerExp.Significand) return 1; + + int diffExponent = biggerExp.Exponent - smallerExp.Exponent; + if (diffExponent < NumberDigitsPrecision) + { + int factor = Int32Powers10[diffExponent]; + int quotient = smallerExp.Significand / biggerExp.Significand; + int remainder = smallerExp.Significand % biggerExp.Significand; + + if (quotient < factor) return 1; + if (quotient > factor) return -1; + if (remainder > 0) return -1; + return 0; + } + + return 1; + } + } + + public bool Equals(Decimal32 other) + { + return CoreCompareTo(other) == 0; + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + return obj is Decimal32 && Equals((Decimal32)obj); + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + static int IDecimalIeee754ParseAndFormatInfo.NumberDigitsPrecision => NumberDigitsPrecision; + + 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.MostSignificantBitNumberOfSignificand => 23; + + static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 32; + + static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 11; + + static uint IDecimalIeee754ConstructorInfo.PositiveInfinityBits => PositiveInfinityValue; + + static uint IDecimalIeee754ConstructorInfo.NegativeInfinityBits => NegativeInfinityValue; + + static uint IDecimalIeee754ConstructorInfo.Zero => ZeroValue; + + static uint IDecimalIeee754UnpackInfo.SignMask => 0x8000_0000; + + static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 32; + + static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; + + static int IDecimalIeee754UnpackInfo.Bias => Bias; + + static int IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 8388608; + + static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent; + + static int IDecimalIeee754UnpackInfo.ConvertToExponent(uint value) => (int)value; + static int IDecimalIeee754UnpackInfo.ConvertToSignificand(uint value) => (int)value; + } +} 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..d8475728886c2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; + +namespace System.Numerics +{ + public readonly struct Decimal64 + : IComparable, + IEquatable + { + public Decimal64(long significand, int exponent) + { + + } + + 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) + { + throw new NotImplementedException(); + } + + 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 int CompareTo(object? value) => throw new NotImplementedException(); + public bool Equals(Decimal64 other) => throw new NotImplementedException(); + + public override bool Equals([NotNullWhen(true)] object? obj) + { + return obj is Decimal64 && Equals((Decimal64)obj); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + } +} From cee05c8c68b713e980bfb40864e1fe23d961ab3e Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Fri, 5 Apr 2024 23:32:51 +0700 Subject: [PATCH 03/23] add test cases --- .../Common/src/System/Number.NumberBuffer.cs | 2 + .../src/System/Number.DecimalIeee754.cs | 62 ++++- .../src/System/Number.Parsing.cs | 46 +++- .../src/System/Numerics/Decimal32.cs | 91 +++----- .../src/System/Numerics/Decimal64.cs | 142 +++++++++++- .../System/Decimal32Tests.cs | 218 ++++++++++++++++++ .../System/Decimal64Tests.cs | 218 ++++++++++++++++++ 7 files changed, 708 insertions(+), 71 deletions(-) create mode 100644 src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs create mode 100644 src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs index 5b4fc7a7564e8..2d21109a7868f 100644 --- a/src/libraries/Common/src/System/Number.NumberBuffer.cs +++ b/src/libraries/Common/src/System/Number.NumberBuffer.cs @@ -21,6 +21,8 @@ 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; // 96 for the longest input + 1 for rounding internal unsafe ref struct NumberBuffer { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 5ecaf801e3da8..f074bc10f0e53 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Numerics; -using static System.Number; namespace System { @@ -35,10 +34,12 @@ internal interface IDecimalIeee754UnpackInfo static abstract TValue SignMask { get; } static abstract int NumberBitsEncoding { get; } static abstract int NumberBitsExponent { get; } + static abstract int NumberDigitsPrecision { get; } static abstract int Bias { get; } static abstract TSignificand TwoPowerMostSignificantBitNumberOfSignificand { get; } static abstract int ConvertToExponent(TValue value); static abstract TSignificand ConvertToSignificand(TValue value); + static abstract TSignificand Power10(int exponent); } internal static partial class Number @@ -115,6 +116,7 @@ internal static TValue CalDecimalIeee754(TSignif throw new OverflowException(SR.Overflow_Decimal); } unsignedSignificand *= TDecimal.Power10(numberZeroDigits); + exponent -= numberZeroDigits; } exponent += TDecimal.Bias; @@ -188,5 +190,63 @@ internal static DecimalIeee754 UnpackDecimalIeee754(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.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index bfe2edc07aaaf..b711448a5e39e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -91,6 +91,7 @@ internal interface IDecimalIeee754ParseAndFormatInfo where TSelf : unmanaged, IDecimalIeee754ParseAndFormatInfo { static abstract int NumberDigitsPrecision { get; } + static abstract int MaxScale { get; } } internal static partial class Number @@ -1003,6 +1004,22 @@ internal static Decimal32 ParseDecimal32(ReadOnlySpan value, Numbe return result; } + internal static Decimal64 ParseDecimal64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + where TChar : unmanaged, IUtfChar + { + ParsingStatus status = TryParseDecimal64(value, styles, info, out Decimal64 result); + if (status != ParsingStatus.OK) + { + if (status == ParsingStatus.Failed) + { + ThrowFormatException(value); + } + ThrowOverflowException(SR.Overflow_Decimal); + } + + return result; + } + internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref decimal value) { number.CheckConsistency(); @@ -1139,6 +1156,11 @@ internal static unsafe bool TryNumberToDecimalIeee754(re return true; } + if (number.Scale > TDecimal.MaxScale) + { + return false; + } + int digitIndex = 0; while (digitIndex < TDecimal.NumberDigitsPrecision && c != 0) @@ -1216,7 +1238,7 @@ internal static ParsingStatus TryParseDecimal(ReadOnlySpan value, internal static ParsingStatus TryParseDecimal32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal32 result) where TChar : unmanaged, IUtfChar { - NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[DecimalNumberBufferLength]); + NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[Decimal32NumberBufferLength]); result = new Decimal32(0, 0); @@ -1235,6 +1257,28 @@ internal static ParsingStatus TryParseDecimal32(ReadOnlySpan value return ParsingStatus.OK; } + internal static ParsingStatus TryParseDecimal64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal64 result) + where TChar : unmanaged, IUtfChar + { + NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[Decimal64NumberBufferLength]); + + result = new Decimal64(0, 0); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberToDecimalIeee754(ref number, out long significand, out int exponent)) + { + return ParsingStatus.Overflow; + } + + result = new Decimal64(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/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 10cf04cb5af39..110de29273518 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -27,8 +27,6 @@ public readonly struct Decimal32 private const uint NegativeInfinityValue = 0xf8000000; private const uint ZeroValue = 0x00000000; private const int MaxSignificand = 9_999_999; - private static readonly Decimal32 PositiveInfinity = new Decimal32(PositiveInfinityValue); - private static readonly Decimal32 NegativeInfinity = new Decimal32(NegativeInfinityValue); public Decimal32(int significand, int exponent) { @@ -62,6 +60,26 @@ public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? pro } 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.TryParseDecimal32(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 = new Decimal32(0, 0); + return false; + } + return Number.TryParseDecimal32(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + } private static ReadOnlySpan Int32Powers10 => [ @@ -86,68 +104,12 @@ public int CompareTo(object? value) throw new ArgumentException(SR.Arg_MustBeDecimal32); } - return CoreCompareTo(i); - } - - private int CoreCompareTo(Decimal32 obj) - { - if (obj._value == _value) - { - return 0; - } - - Number.DecimalIeee754 current = Number.UnpackDecimalIeee754(_value); - Number.DecimalIeee754 other = Number.UnpackDecimalIeee754(obj._value); - - 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(Number.DecimalIeee754 biggerExp, Number.DecimalIeee754 smallerExp) - { - if (biggerExp.Significand >= smallerExp.Significand) return 1; - - int diffExponent = biggerExp.Exponent - smallerExp.Exponent; - if (diffExponent < NumberDigitsPrecision) - { - int factor = Int32Powers10[diffExponent]; - int quotient = smallerExp.Significand / biggerExp.Significand; - int remainder = smallerExp.Significand % biggerExp.Significand; - - if (quotient < factor) return 1; - if (quotient > factor) return -1; - if (remainder > 0) return -1; - return 0; - } - - return 1; - } + return Number.CompareDecimalIeee754(_value, i._value); } public bool Equals(Decimal32 other) { - return CoreCompareTo(other) == 0; + return Number.CompareDecimalIeee754(_value, other._value) == 0; } public override bool Equals([NotNullWhen(true)] object? obj) @@ -162,6 +124,8 @@ public override int GetHashCode() 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]; @@ -185,6 +149,8 @@ public override int GetHashCode() static uint IDecimalIeee754ConstructorInfo.NegativeInfinityBits => NegativeInfinityValue; + static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent; + static uint IDecimalIeee754ConstructorInfo.Zero => ZeroValue; static uint IDecimalIeee754UnpackInfo.SignMask => 0x8000_0000; @@ -197,9 +163,12 @@ public override int GetHashCode() static int IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 8388608; - static int IDecimalIeee754ConstructorInfo.NumberBitsExponent => NumberBitsExponent; + 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]; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index d8475728886c2..03327da7aea4f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -1,21 +1,39 @@ // 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.Collections.Generic; +using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Text; namespace System.Numerics { public readonly struct Decimal64 : IComparable, - IEquatable + IEquatable, + IDecimalIeee754ParseAndFormatInfo, + IDecimalIeee754ConstructorInfo, + IDecimalIeee754UnpackInfo { + 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 = 0x7800000000000000; + private const ulong NegativeInfinityValue = 0xf800000000000000; + private const ulong ZeroValue = 0x0000000000000000; + private const long MaxSignificand = 9_999_999_999_999_999; + 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); @@ -28,8 +46,28 @@ public Decimal64(long significand, int exponent) public static Decimal64 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Number, IFormatProvider? provider = null) { - throw new NotImplementedException(); + 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) { @@ -39,8 +77,47 @@ public static Decimal64 Parse(string s, NumberStyles style, IFormatProvider? pro } return Parse(s.AsSpan(), style, provider); } - public int CompareTo(object? value) => throw new NotImplementedException(); - public bool Equals(Decimal64 other) => throw new NotImplementedException(); + + 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.TryParseDecimal64(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 = new Decimal64(0, 0); + return false; + } + return Number.TryParseDecimal64(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 Decimal64 i) + { + throw new ArgumentException(SR.Arg_MustBeDecimal32); + } + + return Number.CompareDecimalIeee754(_value, i._value); + } + + public bool Equals(Decimal64 other) + { + return Number.CompareDecimalIeee754(_value, other._value) == 0; + } public override bool Equals([NotNullWhen(true)] object? obj) { @@ -49,7 +126,56 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { - throw new NotImplementedException(); + return _value.GetHashCode(); } + + 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.MostSignificantBitNumberOfSignificand => 53; + + 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 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 => 0x8000_0000_0000_0000; + + static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 64; + + static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; + + static int IDecimalIeee754UnpackInfo.Bias => Bias; + + static long IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 9_007_199_254_740_992; + + static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs new file mode 100644 index 0000000000000..3f73dc49dbb2a --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -0,0 +1,218 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Tests +{ + public class Decimal32Tests + { + public static IEnumerable Parse_Valid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + + NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; + + var customFormat1 = new NumberFormatInfo(); + customFormat1.CurrencySymbol = "$"; + customFormat1.CurrencyGroupSeparator = ","; + + var customFormat2 = new NumberFormatInfo(); + customFormat2.NumberDecimalSeparator = "."; + + var customFormat3 = new NumberFormatInfo(); + customFormat3.NumberGroupSeparator = ","; + + var customFormat4 = new NumberFormatInfo(); + customFormat4.NumberDecimalSeparator = "."; + + yield return new object[] { "-123", defaultStyle, null, new Decimal32(-123, 0) }; + yield return new object[] { "0", defaultStyle, null, new Decimal32(0, 0) }; + yield return new object[] { "123", defaultStyle, null, new Decimal32(123, 0) }; + yield return new object[] { " 123 ", defaultStyle, null, new Decimal32(123, 0) }; + yield return new object[] { (567.89).ToString(), defaultStyle, null, new Decimal32(56789, -2) }; + yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal32(-56789, -2) }; + yield return new object[] { "0.6666666500000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal32(6666666, -7) }; + + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal32(234, -3) }; + yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal32(234, 0) }; + yield return new object[] { "7" + new string('0', 96) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal32(7, 96) }; + yield return new object[] { "07" + new string('0', 96) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal32(7, 96) }; + + yield return new object[] { (123.1).ToString(), NumberStyles.AllowDecimalPoint, null, new Decimal32(1231, -1) }; + yield return new object[] { 1000.ToString("N0"), NumberStyles.AllowThousands, null, new Decimal32(1000, 0) }; + + yield return new object[] { "123", NumberStyles.Any, emptyFormat, new Decimal32(123, 0) }; + yield return new object[] { (123.567).ToString(), NumberStyles.Any, emptyFormat, new Decimal32(123567, -3) }; + yield return new object[] { "123", NumberStyles.Float, emptyFormat, new Decimal32(123, 0) }; + yield return new object[] { "$1000", NumberStyles.Currency, customFormat1, new Decimal32(1, 3) }; + yield return new object[] { "123.123", NumberStyles.Float, customFormat2, new Decimal32(123123, -3) }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, customFormat2, new Decimal32(-123, 0) }; + } + + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse(string value, NumberStyles style, IFormatProvider provider, Decimal32 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal32 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal32.TryParse(value, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal32.Parse(value)); + } + + Assert.Equal(expected, Decimal32.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal32.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal32.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal32.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal32.Parse(value, style)); + Assert.Equal(expected, Decimal32.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_Invalid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + + var customFormat = new NumberFormatInfo(); + customFormat.CurrencySymbol = "$"; + customFormat.NumberDecimalSeparator = "."; + + yield return new object[] { null, defaultStyle, null, typeof(ArgumentNullException) }; + yield return new object[] { "1" + new string('0', 97), defaultStyle, null, typeof(OverflowException) }; + yield return new object[] { "", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { " ", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { "Garbage", defaultStyle, null, typeof(FormatException) }; + + yield return new object[] { "ab", defaultStyle, null, typeof(FormatException) }; // Hex value + yield return new object[] { "(123)", defaultStyle, null, typeof(FormatException) }; // Parentheses + yield return new object[] { 100.ToString("C0"), defaultStyle, null, typeof(FormatException) }; // Currency + + yield return new object[] { (123.456m).ToString(), NumberStyles.Integer, null, typeof(FormatException) }; // Decimal + yield return new object[] { " " + (123.456m).ToString(), NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { (123.456m).ToString() + " ", NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { "1E23", NumberStyles.None, null, typeof(FormatException) }; // Exponent + + yield return new object[] { "ab", NumberStyles.None, null, typeof(FormatException) }; // Hex value + yield return new object[] { " 123 ", NumberStyles.None, null, typeof(FormatException) }; // Trailing and leading whitespace + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal32 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None && (style & NumberStyles.AllowLeadingWhite) == (style & NumberStyles.AllowTrailingWhite)) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.False(Decimal32.TryParse(value, out result)); + Assert.Equal(default(Decimal32), result); + + Assert.Throws(exceptionType, () => Decimal32.Parse(value)); + } + + Assert.Throws(exceptionType, () => Decimal32.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal32.TryParse(value, style, provider, out result)); + Assert.Equal(default(Decimal32), result); + + Assert.Throws(exceptionType, () => Decimal32.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal32.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(default(Decimal32), result); + + Assert.Throws(exceptionType, () => Decimal32.Parse(value, style)); + Assert.Throws(exceptionType, () => Decimal32.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + yield return new object[] { "-123", 1, 3, NumberStyles.Number, null, new Decimal32(123, 0) }; + yield return new object[] { "-123", 0, 3, NumberStyles.Number, null, new Decimal32(-12, 0) }; + yield return new object[] { 1000.ToString("N0"), 0, 4, NumberStyles.AllowThousands, null, new Decimal32(100, 0) }; + yield return new object[] { 1000.ToString("N0"), 2, 3, NumberStyles.AllowThousands, null, new Decimal32(0, 0) }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal32(123, 0) }; + yield return new object[] { "1234567890123456789012345.678456", 1, 4, NumberStyles.Number, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal32(2345, 0) }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, Decimal32 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal32 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal32.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal32.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, Decimal32.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, Decimal32.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(Decimal32.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => Decimal32.Parse(value.AsSpan(), style, provider)); + + Assert.False(Decimal32.TryParse(value.AsSpan(), style, provider, out Decimal32 result)); + Assert.Equal(default, result); + } + } + } +} diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs new file mode 100644 index 0000000000000..af03e90c1f577 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -0,0 +1,218 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Tests +{ + public class Decimal64Tests + { + public static IEnumerable Parse_Valid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + + NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; + + var customFormat1 = new NumberFormatInfo(); + customFormat1.CurrencySymbol = "$"; + customFormat1.CurrencyGroupSeparator = ","; + + var customFormat2 = new NumberFormatInfo(); + customFormat2.NumberDecimalSeparator = "."; + + var customFormat3 = new NumberFormatInfo(); + customFormat3.NumberGroupSeparator = ","; + + var customFormat4 = new NumberFormatInfo(); + customFormat4.NumberDecimalSeparator = "."; + + yield return new object[] { "-123", defaultStyle, null, new Decimal64(-123, 0) }; + yield return new object[] { "0", defaultStyle, null, new Decimal64(0, 0) }; + yield return new object[] { "123", defaultStyle, null, new Decimal64(123, 0) }; + yield return new object[] { " 123 ", defaultStyle, null, new Decimal64(123, 0) }; + yield return new object[] { (567.89).ToString(), defaultStyle, null, new Decimal64(56789, -2) }; + yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal64(-56789, -2) }; + yield return new object[] { "0.6666666666666666500000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal64(6666666666666666, -16) }; + + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal64(234, -3) }; + yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal64(234, 0) }; + yield return new object[] { "7" + new string('0', 384) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal64(7, 384) }; + yield return new object[] { "07" + new string('0', 384) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal64(7, 384) }; + + yield return new object[] { (123.1).ToString(), NumberStyles.AllowDecimalPoint, null, new Decimal64(1231, -1) }; + yield return new object[] { 1000.ToString("N0"), NumberStyles.AllowThousands, null, new Decimal64(1000, 0) }; + + yield return new object[] { "123", NumberStyles.Any, emptyFormat, new Decimal64(123, 0) }; + yield return new object[] { (123.567).ToString(), NumberStyles.Any, emptyFormat, new Decimal64(123567, -3) }; + yield return new object[] { "123", NumberStyles.Float, emptyFormat, new Decimal64(123, 0) }; + yield return new object[] { "$1000", NumberStyles.Currency, customFormat1, new Decimal64(1, 3) }; + yield return new object[] { "123.123", NumberStyles.Float, customFormat2, new Decimal64(123123, -3) }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, customFormat2, new Decimal64(-123, 0) }; + } + + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse(string value, NumberStyles style, IFormatProvider provider, Decimal64 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal64 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal64.TryParse(value, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal64.Parse(value)); + } + + Assert.Equal(expected, Decimal64.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal64.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal64.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal64.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal64.Parse(value, style)); + Assert.Equal(expected, Decimal64.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_Invalid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + + var customFormat = new NumberFormatInfo(); + customFormat.CurrencySymbol = "$"; + customFormat.NumberDecimalSeparator = "."; + + yield return new object[] { null, defaultStyle, null, typeof(ArgumentNullException) }; + yield return new object[] { "1" + new string('0', 385), defaultStyle, null, typeof(OverflowException) }; + yield return new object[] { "", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { " ", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { "Garbage", defaultStyle, null, typeof(FormatException) }; + + yield return new object[] { "ab", defaultStyle, null, typeof(FormatException) }; // Hex value + yield return new object[] { "(123)", defaultStyle, null, typeof(FormatException) }; // Parentheses + yield return new object[] { 100.ToString("C0"), defaultStyle, null, typeof(FormatException) }; // Currency + + yield return new object[] { (123.456m).ToString(), NumberStyles.Integer, null, typeof(FormatException) }; // Decimal + yield return new object[] { " " + (123.456m).ToString(), NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { (123.456m).ToString() + " ", NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { "1E23", NumberStyles.None, null, typeof(FormatException) }; // Exponent + + yield return new object[] { "ab", NumberStyles.None, null, typeof(FormatException) }; // Hex value + yield return new object[] { " 123 ", NumberStyles.None, null, typeof(FormatException) }; // Trailing and leading whitespace + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal64 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None && (style & NumberStyles.AllowLeadingWhite) == (style & NumberStyles.AllowTrailingWhite)) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.False(Decimal64.TryParse(value, out result)); + Assert.Equal(default(Decimal64), result); + + Assert.Throws(exceptionType, () => Decimal64.Parse(value)); + } + + Assert.Throws(exceptionType, () => Decimal64.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal64.TryParse(value, style, provider, out result)); + Assert.Equal(default(Decimal64), result); + + Assert.Throws(exceptionType, () => Decimal64.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal64.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(default(Decimal64), result); + + Assert.Throws(exceptionType, () => Decimal64.Parse(value, style)); + Assert.Throws(exceptionType, () => Decimal64.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + yield return new object[] { "-123", 1, 3, NumberStyles.Number, null, new Decimal64(123, 0) }; + yield return new object[] { "-123", 0, 3, NumberStyles.Number, null, new Decimal64(-12, 0) }; + yield return new object[] { 1000.ToString("N0"), 0, 4, NumberStyles.AllowThousands, null, new Decimal64(100, 0) }; + yield return new object[] { 1000.ToString("N0"), 2, 3, NumberStyles.AllowThousands, null, new Decimal64(0, 0) }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal64(123, 0) }; + yield return new object[] { "1234567890123456789012345.678456", 1, 4, NumberStyles.Number, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal64(2345, 0) }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, Decimal64 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal64 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal64.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal64.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, Decimal64.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, Decimal64.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(Decimal64.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => Decimal64.Parse(value.AsSpan(), style, provider)); + + Assert.False(Decimal64.TryParse(value.AsSpan(), style, provider, out Decimal64 result)); + Assert.Equal(default, result); + } + } + } +} From da72dcd79ef327335ea2a05bf6f25d69d66f4201 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Sun, 7 Apr 2024 00:17:44 +0700 Subject: [PATCH 04/23] Add Decimal128 --- .../Common/src/System/Number.NumberBuffer.cs | 3 +- .../src/Resources/Strings.resx | 9 + .../src/System/Number.Parsing.cs | 42 +++- .../src/System/Numerics/Decimal128.cs | 198 ++++++++++++++++++ .../src/System/Numerics/Decimal32.cs | 5 - .../System/Decimal32Tests.cs | 39 +++- .../System/Decimal64Tests.cs | 38 ++++ 7 files changed, 325 insertions(+), 9 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs index 2d21109a7868f..b6f72b9116103 100644 --- a/src/libraries/Common/src/System/Number.NumberBuffer.cs +++ b/src/libraries/Common/src/System/Number.NumberBuffer.cs @@ -22,7 +22,8 @@ internal static partial class Number 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; // 96 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 186c4dd740530..23b37aad83008 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3191,6 +3191,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/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index b711448a5e39e..48d03e5aebe37 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -998,7 +998,7 @@ internal static Decimal32 ParseDecimal32(ReadOnlySpan value, Numbe { ThrowFormatException(value); } - ThrowOverflowException(SR.Overflow_Decimal); + ThrowOverflowException(SR.Overflow_Decimal32); } return result; @@ -1014,7 +1014,23 @@ internal static Decimal64 ParseDecimal64(ReadOnlySpan value, Numbe { ThrowFormatException(value); } - ThrowOverflowException(SR.Overflow_Decimal); + ThrowOverflowException(SR.Overflow_Decimal64); + } + + return result; + } + + internal static Decimal128 ParseDecimal128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + where TChar : unmanaged, IUtfChar + { + ParsingStatus status = TryParseDecimal128(value, styles, info, out Decimal128 result); + if (status != ParsingStatus.OK) + { + if (status == ParsingStatus.Failed) + { + ThrowFormatException(value); + } + ThrowOverflowException(SR.Overflow_Decimal128); } return result; @@ -1279,6 +1295,28 @@ internal static ParsingStatus TryParseDecimal64(ReadOnlySpan value return ParsingStatus.OK; } + internal static ParsingStatus TryParseDecimal128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal128 result) + where TChar : unmanaged, IUtfChar + { + NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[Decimal128NumberBufferLength]); + + result = new Decimal128(0, 0); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberToDecimalIeee754(ref number, out Int128 significand, out int exponent)) + { + return ParsingStatus.Overflow; + } + + result = new Decimal128(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..455077fba19dc --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -0,0 +1,198 @@ +// 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, + IEquatable, + IDecimalIeee754ParseAndFormatInfo, + IDecimalIeee754ConstructorInfo, + IDecimalIeee754UnpackInfo + { + internal readonly UInt128 _value; + + 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; + + public Decimal128(Int128 significand, int exponent) + { + _value = Number.CalDecimalIeee754(significand, exponent); + } + 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.TryParseDecimal128(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 = new Decimal128(0, 0); + return false; + } + return Number.TryParseDecimal128(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 Decimal128 i) + { + throw new ArgumentException(SR.Arg_MustBeDecimal128); + } + + return Number.CompareDecimalIeee754(_value, i._value); + } + + public bool Equals(Decimal128 other) + { + return Number.CompareDecimalIeee754(_value, other._value) == 0; + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + return obj is Decimal128 && Equals((Decimal128)obj); + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + 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.MostSignificantBitNumberOfSignificand => 113; + + 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 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 => new UInt128(0x8000_0000_0000_0000, 0); + + static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 128; + + static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; + + static int IDecimalIeee754UnpackInfo.Bias => Bias; + + static Int128 IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => new Int128(0x0002_0000_0000_0000, 0); + + static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; + + + 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), + ]; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 110de29273518..19629977401e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -33,11 +33,6 @@ public Decimal32(int significand, int exponent) _value = Number.CalDecimalIeee754(significand, exponent); } - internal Decimal32(uint value) - { - _value = value; - } - 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); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs index 3f73dc49dbb2a..0d686dfcc743f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -201,7 +201,6 @@ public static void Parse_Span_Valid(string value, int offset, int count, NumberS Assert.Equal(expected, result); } - [Theory] [MemberData(nameof(Parse_Invalid_TestData))] public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) @@ -214,5 +213,43 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP Assert.Equal(default, result); } } + + [Fact] + public static void Midpoint_Rounding() + { + var number = new Decimal32(12345685, 0); + Assert.Equal(new Decimal32(1234568, 1), number); + } + + [Fact] + public static void Rounding() + { + var number = new Decimal32(12345678, 0); + Assert.Equal(new Decimal32(1234568, 1), number); + + number = new Decimal32(12345671, 0); + Assert.Equal(new Decimal32(1234567, 1), number); + } + + [Fact] + public static void CompareTo_Other_ReturnsExpected() + { + for (int i = 1; i < 7; i++) + { + var d1 = new Decimal32(1, i); + var d2 = new Decimal32(int.Parse("1" + new string('0', i)), 0); + Assert.Equal(d1, d2); + } + Assert.Equal(new Decimal32(-1, 1), new Decimal32(-10, 0)); + Assert.NotEqual(new Decimal32(1, 1), new Decimal32(-10, 0)); + Assert.NotEqual(new Decimal32(-1, 1), new Decimal32(10, 0)); + } + + [Fact] + public static void GetHashCodeTest() + { + var d = new Decimal32(10, 20); + Assert.Equal(d.GetHashCode(), d.GetHashCode()); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index af03e90c1f577..11d7c45efb8ad 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -214,5 +214,43 @@ public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatP Assert.Equal(default, result); } } + + [Fact] + public static void Midpoint_Rounding() + { + var number = new Decimal64(12345688888888885, 0); + Assert.Equal(new Decimal64(1234568888888888, 1), number); + } + + [Fact] + public static void Rounding() + { + var number = new Decimal64(12345677777777778, 0); + Assert.Equal(new Decimal64(1234567777777778, 1), number); + + number = new Decimal64(12345677777777771, 0); + Assert.Equal(new Decimal64(1234567777777777, 1), number); + } + + [Fact] + public static void CompareTo_Other_ReturnsExpected() + { + for (int i = 1; i < 16; i++) + { + var d1 = new Decimal64(1, i); + var d2 = new Decimal64(long.Parse("1" + new string('0', i)), 0); + Assert.Equal(d1, d2); + } + Assert.Equal(new Decimal64(-1, 1), new Decimal64(-10, 0)); + Assert.NotEqual(new Decimal64(1, 1), new Decimal64(-10, 0)); + Assert.NotEqual(new Decimal64(-1, 1), new Decimal64(10, 0)); + } + + [Fact] + public static void GetHashCodeTest() + { + var d = new Decimal64(10, 20); + Assert.Equal(d.GetHashCode(), d.GetHashCode()); + } } } From 6f3827c6070669e55374904991c6e97472fe81ac Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Sun, 7 Apr 2024 00:25:12 +0700 Subject: [PATCH 05/23] add tests --- .../System.Private.CoreLib.Shared.projitems | 4 + .../System.Runtime/ref/System.Runtime.cs | 66 +++++ .../System.Runtime.Tests.csproj | 3 + .../System/Decimal128Tests.cs | 256 ++++++++++++++++++ 4 files changed, 329 insertions(+) create mode 100644 src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs 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 986102f5475e3..54ec9699a8906 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 @@ -422,6 +422,10 @@ + + + + diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 78482d61ea3f8..52149740b226a 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10582,6 +10582,72 @@ public static void HtmlEncode(string? value, System.IO.TextWriter output) { } } namespace System.Numerics { + public readonly struct Decimal32 + : System.IComparable, + System.IEquatable + { + public Decimal32(int significand, int exponent) { 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 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 s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } + + public int CompareTo(object? value) { throw null; } + public bool Equals(Decimal32 other) { throw null; } + } + + public readonly struct Decimal64 + : System.IComparable, + System.IEquatable + { + public Decimal64(long significand, int exponent) { throw null; } + public static Decimal64 Parse(string s) { throw null; } + public static Decimal64 Parse(string s, System.Globalization.NumberStyles style) { throw null; } + public static Decimal64 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } + public static Decimal64 Parse(string s, IFormatProvider? provider) { throw null; } + public static Decimal64 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Number, IFormatProvider? provider = null) { throw null; } + public static Decimal64 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal64 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, out Decimal64 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } + + public int CompareTo(object? value) { throw null; } + public bool Equals(Decimal64 other) { throw null; } + } + + public readonly struct Decimal128 + : System.IComparable, + System.IEquatable + { + public Decimal128(Int128 significand, int exponent) { throw null; } + public static Decimal128 Parse(string s) { throw null; } + public static Decimal128 Parse(string s, System.Globalization.NumberStyles style) { throw null; } + public static Decimal128 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } + public static Decimal128 Parse(string s, IFormatProvider? provider) { throw null; } + public static Decimal128 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Number, IFormatProvider? provider = null) { throw null; } + public static Decimal128 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal128 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, out Decimal128 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } + public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } + + public int CompareTo(object? value) { throw null; } + public bool Equals(Decimal128 other) { throw null; } + } + public static partial class BitOperations { public static bool IsPow2(int value) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index ca223dc873277..d63c8cd790645 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -68,6 +68,9 @@ + + + diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs new file mode 100644 index 0000000000000..492584cdaa01a --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs @@ -0,0 +1,256 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Tests +{ + public class Decimal128Tests + { + public static IEnumerable Parse_Valid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo; + + NumberFormatInfo emptyFormat = NumberFormatInfo.CurrentInfo; + + var customFormat1 = new NumberFormatInfo(); + customFormat1.CurrencySymbol = "$"; + customFormat1.CurrencyGroupSeparator = ","; + + var customFormat2 = new NumberFormatInfo(); + customFormat2.NumberDecimalSeparator = "."; + + var customFormat3 = new NumberFormatInfo(); + customFormat3.NumberGroupSeparator = ","; + + var customFormat4 = new NumberFormatInfo(); + customFormat4.NumberDecimalSeparator = "."; + + yield return new object[] { "-123", defaultStyle, null, new Decimal128(-123, 0) }; + yield return new object[] { "0", defaultStyle, null, new Decimal128(0, 0) }; + yield return new object[] { "123", defaultStyle, null, new Decimal128(123, 0) }; + yield return new object[] { " 123 ", defaultStyle, null, new Decimal128(123, 0) }; + yield return new object[] { (567.89).ToString(), defaultStyle, null, new Decimal128(56789, -2) }; + yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal128(-56789, -2) }; + yield return new object[] { "0.666666666666666666666666666666666650000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal128(Int128.Parse(new string('6', 34)), -34) }; + + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal128(234, -3) }; + yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal128(234, 0) }; + yield return new object[] { "7" + new string('0', 6144) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal128(7, 6144) }; + yield return new object[] { "07" + new string('0', 6144) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal128(7, 6144) }; + + yield return new object[] { (123.1).ToString(), NumberStyles.AllowDecimalPoint, null, new Decimal128(1231, -1) }; + yield return new object[] { 1000.ToString("N0"), NumberStyles.AllowThousands, null, new Decimal128(1000, 0) }; + + yield return new object[] { "123", NumberStyles.Any, emptyFormat, new Decimal128(123, 0) }; + yield return new object[] { (123.567).ToString(), NumberStyles.Any, emptyFormat, new Decimal128(123567, -3) }; + yield return new object[] { "123", NumberStyles.Float, emptyFormat, new Decimal128(123, 0) }; + yield return new object[] { "$1000", NumberStyles.Currency, customFormat1, new Decimal128(1, 3) }; + yield return new object[] { "123.123", NumberStyles.Float, customFormat2, new Decimal128(123123, -3) }; + yield return new object[] { "(123)", NumberStyles.AllowParentheses, customFormat2, new Decimal128(-123, 0) }; + } + + + [Theory] + [MemberData(nameof(Parse_Valid_TestData))] + public static void Parse(string value, NumberStyles style, IFormatProvider provider, Decimal128 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal128 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal128.TryParse(value, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal128.Parse(value)); + } + + Assert.Equal(expected, Decimal128.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal128.TryParse(value, style, provider, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal128.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.True(Decimal128.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal128.Parse(value, style)); + Assert.Equal(expected, Decimal128.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_Invalid_TestData() + { + NumberStyles defaultStyle = NumberStyles.Number; + + var customFormat = new NumberFormatInfo(); + customFormat.CurrencySymbol = "$"; + customFormat.NumberDecimalSeparator = "."; + + yield return new object[] { null, defaultStyle, null, typeof(ArgumentNullException) }; + yield return new object[] { "1" + new string('0', 6145), defaultStyle, null, typeof(OverflowException) }; + yield return new object[] { "", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { " ", defaultStyle, null, typeof(FormatException) }; + yield return new object[] { "Garbage", defaultStyle, null, typeof(FormatException) }; + + yield return new object[] { "ab", defaultStyle, null, typeof(FormatException) }; // Hex value + yield return new object[] { "(123)", defaultStyle, null, typeof(FormatException) }; // Parentheses + yield return new object[] { 100.ToString("C0"), defaultStyle, null, typeof(FormatException) }; // Currency + + yield return new object[] { (123.456m).ToString(), NumberStyles.Integer, null, typeof(FormatException) }; // Decimal + yield return new object[] { " " + (123.456m).ToString(), NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { (123.456m).ToString() + " ", NumberStyles.None, null, typeof(FormatException) }; // Leading space + yield return new object[] { "1E23", NumberStyles.None, null, typeof(FormatException) }; // Exponent + + yield return new object[] { "ab", NumberStyles.None, null, typeof(FormatException) }; // Hex value + yield return new object[] { " 123 ", NumberStyles.None, null, typeof(FormatException) }; // Trailing and leading whitespace + } + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal128 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None && (style & NumberStyles.AllowLeadingWhite) == (style & NumberStyles.AllowTrailingWhite)) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.False(Decimal128.TryParse(value, out result)); + Assert.Equal(default(Decimal128), result); + + Assert.Throws(exceptionType, () => Decimal128.Parse(value)); + } + + Assert.Throws(exceptionType, () => Decimal128.Parse(value, provider)); + } + + // Use Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal128.TryParse(value, style, provider, out result)); + Assert.Equal(default(Decimal128), result); + + Assert.Throws(exceptionType, () => Decimal128.Parse(value, style, provider)); + + if (isDefaultProvider) + { + // Use Parse(string, NumberStyles) or Parse(string, NumberStyles, IFormatProvider) + Assert.False(Decimal128.TryParse(value, style, NumberFormatInfo.CurrentInfo, out result)); + Assert.Equal(default(Decimal128), result); + + Assert.Throws(exceptionType, () => Decimal128.Parse(value, style)); + Assert.Throws(exceptionType, () => Decimal128.Parse(value, style, NumberFormatInfo.CurrentInfo)); + } + } + + public static IEnumerable Parse_ValidWithOffsetCount_TestData() + { + foreach (object[] inputs in Parse_Valid_TestData()) + { + yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] }; + } + + yield return new object[] { "-123", 1, 3, NumberStyles.Number, null, new Decimal128(123, 0) }; + yield return new object[] { "-123", 0, 3, NumberStyles.Number, null, new Decimal128(-12, 0) }; + yield return new object[] { 1000.ToString("N0"), 0, 4, NumberStyles.AllowThousands, null, new Decimal128(100, 0) }; + yield return new object[] { 1000.ToString("N0"), 2, 3, NumberStyles.AllowThousands, null, new Decimal128(0, 0) }; + yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal128(123, 0) }; + yield return new object[] { "1234567890123456789012345.678456", 1, 4, NumberStyles.Number, new NumberFormatInfo() { NumberDecimalSeparator = "." }, new Decimal128(2345, 0) }; + } + + [Theory] + [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))] + public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, Decimal128 expected) + { + bool isDefaultProvider = provider == null || provider == NumberFormatInfo.CurrentInfo; + Decimal128 result; + if ((style & ~NumberStyles.Number) == 0 && style != NumberStyles.None) + { + // Use Parse(string) or Parse(string, IFormatProvider) + if (isDefaultProvider) + { + Assert.True(Decimal128.TryParse(value.AsSpan(offset, count), out result)); + Assert.Equal(expected, result); + + Assert.Equal(expected, Decimal128.Parse(value.AsSpan(offset, count))); + } + + Assert.Equal(expected, Decimal128.Parse(value.AsSpan(offset, count), provider: provider)); + } + + Assert.Equal(expected, Decimal128.Parse(value.AsSpan(offset, count), style, provider)); + + Assert.True(Decimal128.TryParse(value.AsSpan(offset, count), style, provider, out result)); + Assert.Equal(expected, result); + } + + + [Theory] + [MemberData(nameof(Parse_Invalid_TestData))] + public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType) + { + if (value != null) + { + Assert.Throws(exceptionType, () => Decimal128.Parse(value.AsSpan(), style, provider)); + + Assert.False(Decimal128.TryParse(value.AsSpan(), style, provider, out Decimal128 result)); + Assert.Equal(default, result); + } + } + + [Fact] + public static void Midpoint_Rounding() + { + var number = new Decimal128(Int128.Parse("12345688888888881234568888888888885"), 0); + Assert.Equal(new Decimal128(Int128.Parse("1234568888888888123456888888888888"), 1), number); + } + + [Fact] + public static void Rounding() + { + var number = new Decimal128(Int128.Parse("12345677777777771234567777777777778"), 0); + Assert.Equal(new Decimal128(Int128.Parse("1234567777777777123456777777777778"), 1), number); + + number = new Decimal128(Int128.Parse("12345677777777771234567777777777771"), 0); + Assert.Equal(new Decimal128(Int128.Parse("1234567777777777123456777777777777"), 1), number); + } + + [Fact] + public static void CompareTo_Other_ReturnsExpected() + { + for (int i = 1; i < 30; i++) + { + var d1 = new Decimal128(1, i); + var d2 = new Decimal128(Int128.Parse("1" + new string('0', i)), 0); + Assert.Equal(d1, d2); + } + Assert.Equal(new Decimal128(-1, 1), new Decimal128(-10, 0)); + Assert.NotEqual(new Decimal128(1, 1), new Decimal128(-10, 0)); + Assert.NotEqual(new Decimal128(-1, 1), new Decimal128(10, 0)); + } + + [Fact] + public static void GetHashCodeTest() + { + var d = new Decimal128(10, 20); + Assert.Equal(d.GetHashCode(), d.GetHashCode()); + } + } +} From f882b963f89b7a6946454f32942f77d88367922d Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Sun, 7 Apr 2024 08:20:02 +0700 Subject: [PATCH 06/23] Fix digits pointer --- .../System.Private.CoreLib/src/System/Number.Parsing.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3d63294bf9331..2a644d117625d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -902,7 +902,7 @@ internal static unsafe bool TryNumberToDecimalIeee754(re { number.CheckConsistency(); - byte* p = number.GetDigitsPointer(); + byte* p = number.DigitsPtr; int c = *p; significand = TSignificand.Zero; exponent = 0; From 762f56dd3c1ba49b40458d14a6f9c6a01930453e Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Tue, 9 Apr 2024 23:22:28 +0700 Subject: [PATCH 07/23] Implement ToString() method. --- .../src/System/Number.Formatting.cs | 201 ++++++++++++++++++ .../src/System/Number.Parsing.cs | 3 + .../src/System/Numerics/Decimal128.cs | 18 ++ .../src/System/Numerics/Decimal32.cs | 17 ++ .../src/System/Numerics/Decimal64.cs | 17 ++ .../System.Runtime/ref/System.Runtime.cs | 38 +++- .../System/Decimal128Tests.cs | 54 +++++ .../System/Decimal32Tests.cs | 54 +++++ .../System/Decimal64Tests.cs | 54 +++++ 9 files changed, 448 insertions(+), 8 deletions(-) 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 ad75d88cbda7c..98a4b23377589 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -314,6 +314,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); @@ -367,6 +445,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(d._value); + 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 2a644d117625d..24884b2244b3e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -102,6 +102,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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index 455077fba19dc..fc466a2fbd866 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -105,6 +105,24 @@ public override int GetHashCode() { return _value.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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 19629977401e6..1e0c04e57bed5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -117,6 +117,23 @@ 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index 03327da7aea4f..7e6f95ed0d68a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -129,6 +129,23 @@ 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; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 301a1d7029c87..0c99b61663219 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10618,21 +10618,28 @@ public readonly struct Decimal32 System.IEquatable { public Decimal32(int significand, int exponent) { throw null; } + + public int CompareTo(object? value) { 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 s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } - - public int CompareTo(object? value) { throw null; } - public bool Equals(Decimal32 other) { throw null; } } public readonly struct Decimal64 @@ -10640,12 +10647,22 @@ public readonly struct Decimal64 System.IEquatable { public Decimal64(long significand, int exponent) { throw null; } + + public int CompareTo(object? value) { throw null; } + public bool Equals(Decimal64 other) { throw null; } + public static Decimal64 Parse(string s) { throw null; } public static Decimal64 Parse(string s, System.Globalization.NumberStyles style) { throw null; } public static Decimal64 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } public static Decimal64 Parse(string s, IFormatProvider? provider) { throw null; } public static Decimal64 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Number, IFormatProvider? provider = null) { throw null; } public static Decimal64 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 Decimal64 result) { throw null; } public static bool TryParse(ReadOnlySpan s, out Decimal64 result) { throw null; } public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } @@ -10653,8 +10670,6 @@ public readonly struct Decimal64 public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } - public int CompareTo(object? value) { throw null; } - public bool Equals(Decimal64 other) { throw null; } } public readonly struct Decimal128 @@ -10662,21 +10677,28 @@ public readonly struct Decimal128 System.IEquatable { public Decimal128(Int128 significand, int exponent) { throw null; } + + public int CompareTo(object? value) { throw null; } + public bool Equals(Decimal128 other) { throw null; } + public static Decimal128 Parse(string s) { throw null; } public static Decimal128 Parse(string s, System.Globalization.NumberStyles style) { throw null; } public static Decimal128 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } public static Decimal128 Parse(string s, IFormatProvider? provider) { throw null; } public static Decimal128 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Number, IFormatProvider? provider = null) { throw null; } public static Decimal128 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 Decimal128 result) { throw null; } public static bool TryParse(ReadOnlySpan s, out Decimal128 result) { throw null; } public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } - - public int CompareTo(object? value) { throw null; } - public bool Equals(Decimal128 other) { throw null; } } public static partial class BitOperations diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs index 492584cdaa01a..11106dbe27a75 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs @@ -252,5 +252,59 @@ public static void GetHashCodeTest() var d = new Decimal128(10, 20); Assert.Equal(d.GetHashCode(), d.GetHashCode()); } + + public static IEnumerable ToString_TestData() + { + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { new Decimal128(3, 6144), "G", defaultFormat, "3" + new string('0', 6144) }; + yield return new object[] { new Decimal128(-3, 6144), "G", defaultFormat, "-3" + new string('0', 6144) }; + yield return new object[] { new Decimal128(-4567, 0), "G", defaultFormat, "-4567" }; + yield return new object[] { new Decimal128(-4567891, -3), "G", defaultFormat, "-4567.891" }; + yield return new object[] { new Decimal128(0, 0), "G", defaultFormat, "0" }; + yield return new object[] { new Decimal128(4567, 0), "G", defaultFormat, "4567" }; + yield return new object[] { new Decimal128(4567891, -3), "G", defaultFormat, "4567.891" }; + + yield return new object[] { new Decimal128(2468, 0), "N", defaultFormat, "2,468.00" }; + + yield return new object[] { new Decimal128(2467, 0), "[#-##-#]", defaultFormat, "[2-46-7]" }; + + } + } + + [Fact] + public static void Test_ToString() + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + foreach (object[] testdata in ToString_TestData()) + { + ToString((Decimal128)testdata[0], (string)testdata[1], (IFormatProvider)testdata[2], (string)testdata[3]); + } + } + } + + private static void ToString(Decimal128 f, string format, IFormatProvider provider, string expected) + { + bool isDefaultProvider = provider == null; + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(expected, f.ToString()); + Assert.Equal(expected, f.ToString((IFormatProvider)null)); + } + Assert.Equal(expected, f.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant())); // If format is upper case, then exponents are printed in upper case + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant())); // If format is lower case, then exponents are printed in lower case + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), null)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), null)); + } + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), provider)); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs index 0d686dfcc743f..4de487be22a7d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -251,5 +251,59 @@ public static void GetHashCodeTest() var d = new Decimal32(10, 20); Assert.Equal(d.GetHashCode(), d.GetHashCode()); } + + public static IEnumerable ToString_TestData() + { + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { new Decimal32(3, 96), "G", defaultFormat, "3" + new string('0', 96) }; + yield return new object[] { new Decimal32(-3, 96), "G", defaultFormat, "-3" + new string('0', 96) }; + yield return new object[] { new Decimal32(-4567, 0), "G", defaultFormat, "-4567" }; + yield return new object[] { new Decimal32(-4567891, -3), "G", defaultFormat, "-4567.891" }; + yield return new object[] { new Decimal32(0, 0), "G", defaultFormat, "0" }; + yield return new object[] { new Decimal32(4567, 0), "G", defaultFormat, "4567" }; + yield return new object[] { new Decimal32(4567891, -3), "G", defaultFormat, "4567.891" }; + + yield return new object[] { new Decimal32(2468, 0), "N", defaultFormat, "2,468.00" }; + + yield return new object[] { new Decimal32(2467, 0), "[#-##-#]", defaultFormat, "[2-46-7]" }; + + } + } + + [Fact] + public static void Test_ToString() + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + foreach (object[] testdata in ToString_TestData()) + { + ToString((Decimal32)testdata[0], (string)testdata[1], (IFormatProvider)testdata[2], (string)testdata[3]); + } + } + } + + private static void ToString(Decimal32 f, string format, IFormatProvider provider, string expected) + { + bool isDefaultProvider = provider == null; + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(expected, f.ToString()); + Assert.Equal(expected, f.ToString((IFormatProvider)null)); + } + Assert.Equal(expected, f.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant())); // If format is upper case, then exponents are printed in upper case + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant())); // If format is lower case, then exponents are printed in lower case + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), null)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), null)); + } + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), provider)); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index 11d7c45efb8ad..d8a6cd14aeb0e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -252,5 +252,59 @@ public static void GetHashCodeTest() var d = new Decimal64(10, 20); Assert.Equal(d.GetHashCode(), d.GetHashCode()); } + + public static IEnumerable ToString_TestData() + { + foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo }) + { + yield return new object[] { new Decimal64(3, 384), "G", defaultFormat, "3" + new string('0', 384) }; + yield return new object[] { new Decimal64(-3, 384), "G", defaultFormat, "-3" + new string('0', 384) }; + yield return new object[] { new Decimal64(-4567, 0), "G", defaultFormat, "-4567" }; + yield return new object[] { new Decimal64(-4567891, -3), "G", defaultFormat, "-4567.891" }; + yield return new object[] { new Decimal64(0, 0), "G", defaultFormat, "0" }; + yield return new object[] { new Decimal64(4567, 0), "G", defaultFormat, "4567" }; + yield return new object[] { new Decimal64(4567891, -3), "G", defaultFormat, "4567.891" }; + + yield return new object[] { new Decimal64(2468, 0), "N", defaultFormat, "2,468.00" }; + + yield return new object[] { new Decimal64(2467, 0), "[#-##-#]", defaultFormat, "[2-46-7]" }; + + } + } + + [Fact] + public static void Test_ToString() + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture)) + { + foreach (object[] testdata in ToString_TestData()) + { + ToString((Decimal64)testdata[0], (string)testdata[1], (IFormatProvider)testdata[2], (string)testdata[3]); + } + } + } + + private static void ToString(Decimal64 f, string format, IFormatProvider provider, string expected) + { + bool isDefaultProvider = provider == null; + if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() == "G") + { + if (isDefaultProvider) + { + Assert.Equal(expected, f.ToString()); + Assert.Equal(expected, f.ToString((IFormatProvider)null)); + } + Assert.Equal(expected, f.ToString(provider)); + } + if (isDefaultProvider) + { + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant())); // If format is upper case, then exponents are printed in upper case + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant())); // If format is lower case, then exponents are printed in lower case + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), null)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), null)); + } + Assert.Equal(expected.Replace('e', 'E'), f.ToString(format.ToUpperInvariant(), provider)); + Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), provider)); + } } } From 66a112736011294868ef7a4e84d2f6ce0a36c9e3 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 10 Apr 2024 00:03:20 +0700 Subject: [PATCH 08/23] resolve comments --- .../src/System/Number.Parsing.cs | 73 +++++-------------- .../src/System/Numerics/Decimal128.cs | 15 ++-- .../src/System/Numerics/Decimal32.cs | 16 +++- .../src/System/Numerics/Decimal64.cs | 16 +++- 4 files changed, 54 insertions(+), 66 deletions(-) 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 24884b2244b3e..1bb9ebb279b2c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Unicode; +using static System.Number; namespace System { @@ -94,6 +95,15 @@ internal interface IDecimalIeee754ParseAndFormatInfo 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 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; @@ -734,7 +744,7 @@ internal static decimal ParseDecimal(ReadOnlySpan value, NumberSty internal static Decimal32 ParseDecimal32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { - ParsingStatus status = TryParseDecimal32(value, styles, info, out Decimal32 result); + ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal32 result); if (status != ParsingStatus.OK) { if (status == ParsingStatus.Failed) @@ -750,7 +760,7 @@ internal static Decimal32 ParseDecimal32(ReadOnlySpan value, Numbe internal static Decimal64 ParseDecimal64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { - ParsingStatus status = TryParseDecimal64(value, styles, info, out Decimal64 result); + ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal64 result); if (status != ParsingStatus.OK) { if (status == ParsingStatus.Failed) @@ -766,7 +776,7 @@ internal static Decimal64 ParseDecimal64(ReadOnlySpan value, Numbe internal static Decimal128 ParseDecimal128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { - ParsingStatus status = TryParseDecimal128(value, styles, info, out Decimal128 result); + ParsingStatus status = TryParseDecimalIeee754(value, styles, info, out Decimal128 result); if (status != ParsingStatus.OK) { if (status == ParsingStatus.Failed) @@ -994,68 +1004,25 @@ internal static ParsingStatus TryParseDecimal(ReadOnlySpan value, return ParsingStatus.OK; } - internal static ParsingStatus TryParseDecimal32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal32 result) - where TChar : unmanaged, IUtfChar - { - NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[Decimal32NumberBufferLength]); - - result = new Decimal32(0, 0); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToDecimalIeee754(ref number, out int significand, out int exponent)) - { - return ParsingStatus.Overflow; - } - - result = new Decimal32(number.IsNegative ? -significand : significand, exponent); - - return ParsingStatus.OK; - } - - internal static ParsingStatus TryParseDecimal64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal64 result) - where TChar : unmanaged, IUtfChar - { - NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[Decimal64NumberBufferLength]); - - result = new Decimal64(0, 0); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberToDecimalIeee754(ref number, out long significand, out int exponent)) - { - return ParsingStatus.Overflow; - } - - result = new Decimal64(number.IsNegative ? -significand : significand, exponent); - - return ParsingStatus.OK; - } - - internal static ParsingStatus TryParseDecimal128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Decimal128 result) + 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[Decimal128NumberBufferLength]); - - result = new Decimal128(0, 0); + NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[TDecimal.DecimalNumberBufferLength]); + result = default; if (!TryStringToNumber(value, styles, ref number, info)) { return ParsingStatus.Failed; } - if (!TryNumberToDecimalIeee754(ref number, out Int128 significand, out int exponent)) + if (!TDecimal.TryNumberToDecimalIeee754(ref number, out TSignificand significand, out int exponent)) { return ParsingStatus.Overflow; } - result = new Decimal128(number.IsNegative ? -significand : significand, exponent); + result = TDecimal.Construct(number.IsNegative ? -significand : significand, exponent); return ParsingStatus.OK; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index fc466a2fbd866..390c5767cdf64 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -15,7 +15,8 @@ public readonly struct Decimal128 IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, - IDecimalIeee754UnpackInfo + IDecimalIeee754UnpackInfo, + IDecimalIeee754TryParseInfo { internal readonly UInt128 _value; @@ -62,7 +63,7 @@ public static Decimal128 Parse(string s, NumberStyles style, IFormatProvider? pr public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal128 result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseDecimal128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + 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) { @@ -70,10 +71,10 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I if (s == null) { - result = new Decimal128(0, 0); + result = default; return false; } - return Number.TryParseDecimal128(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public int CompareTo(object? value) @@ -160,7 +161,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static Int128 IDecimalIeee754UnpackInfo.ConvertToSignificand(UInt128 value) => (Int128)value; static Int128 IDecimalIeee754UnpackInfo.Power10(int exponent) => Int128Powers10[exponent]; - + static UInt128 IDecimalIeee754UnpackInfo.SignMask => new UInt128(0x8000_0000_0000_0000, 0); static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 128; @@ -212,5 +213,9 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin 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 index 1e0c04e57bed5..da883e28fa455 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -14,7 +14,8 @@ public readonly struct Decimal32 IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, - IDecimalIeee754UnpackInfo + IDecimalIeee754UnpackInfo, + IDecimalIeee754TryParseInfo { internal readonly uint _value; @@ -62,7 +63,7 @@ public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? pro public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseDecimal32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + 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) { @@ -70,10 +71,10 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I if (s == null) { - result = new Decimal32(0, 0); + result = default; return false; } - return Number.TryParseDecimal32(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } private static ReadOnlySpan Int32Powers10 => @@ -182,5 +183,12 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754UnpackInfo.ConvertToSignificand(uint value) => (int)value; static int IDecimalIeee754UnpackInfo.Power10(int exponent) => Int32Powers10[exponent]; + + 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 index 7e6f95ed0d68a..24e83ea497347 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -12,7 +12,8 @@ public readonly struct Decimal64 IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, - IDecimalIeee754UnpackInfo + IDecimalIeee754UnpackInfo, + IDecimalIeee754TryParseInfo { internal readonly ulong _value; @@ -85,7 +86,7 @@ public static Decimal64 Parse(string s, NumberStyles style, IFormatProvider? pro public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal64 result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseDecimal64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + 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) { @@ -93,10 +94,10 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I if (s == null) { - result = new Decimal64(0, 0); + result = default; return false; } - return Number.TryParseDecimal64(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimalIeee754(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public int CompareTo(object? value) @@ -194,5 +195,12 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static long IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 9_007_199_254_740_992; static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; + + 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); } } From 83937d2d79254ba59577697a3b0410d31a6bf4d1 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 10 Apr 2024 00:17:13 +0700 Subject: [PATCH 09/23] resolve comments --- .../src/System/Number.DecimalIeee754.cs | 5 ++--- .../System.Private.CoreLib/src/System/Numerics/Decimal128.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index f074bc10f0e53..025a00bab09b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -85,12 +85,11 @@ internal static TValue CalDecimalIeee754(TSignif } exponent += numberDigitsRemoving; - TSignificand two = TSignificand.CreateTruncating(2); TSignificand divisor = TDecimal.Power10(numberDigitsRemoving); TSignificand quotient = unsignedSignificand / divisor; TSignificand remainder = unsignedSignificand % divisor; - TSignificand midPoint = divisor / two; - bool needRouding = remainder > midPoint || (remainder == midPoint && quotient % two == TSignificand.One); + TSignificand midPoint = divisor >> 1; + bool needRouding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One); if (needRouding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index 390c5767cdf64..e3c373cbea2f5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -161,7 +161,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static Int128 IDecimalIeee754UnpackInfo.ConvertToSignificand(UInt128 value) => (Int128)value; static Int128 IDecimalIeee754UnpackInfo.Power10(int exponent) => Int128Powers10[exponent]; - + static UInt128 IDecimalIeee754UnpackInfo.SignMask => new UInt128(0x8000_0000_0000_0000, 0); static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 128; From 19755ba6288e2ab48336a1ba5408b54c97e68032 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 10 Apr 2024 13:04:36 +0700 Subject: [PATCH 10/23] Refactor shift operators to mask --- .../src/System/Number.DecimalIeee754.cs | 22 ++++++++++--------- .../src/System/Numerics/Decimal128.cs | 20 ++++++++++++----- .../src/System/Numerics/Decimal32.cs | 20 ++++++++++++----- .../src/System/Numerics/Decimal64.cs | 19 +++++++++++----- 4 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 025a00bab09b4..85012d2834d1b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -32,11 +32,15 @@ internal interface IDecimalIeee754UnpackInfo where TValue : IBinaryInteger { static abstract TValue SignMask { get; } - static abstract int NumberBitsEncoding { get; } - static abstract int NumberBitsExponent { 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 TSignificand TwoPowerMostSignificantBitNumberOfSignificand { get; } + static abstract TValue MostSignificantBitOfSignificandMask { get; } static abstract int ConvertToExponent(TValue value); static abstract TSignificand ConvertToSignificand(TValue value); static abstract TSignificand Power10(int exponent); @@ -171,20 +175,18 @@ internal static DecimalIeee754 UnpackDecimalIeee754 { bool signed = (value & TDecimal.SignMask) != TValue.Zero; - TValue g0g1Bits = (value << 1) >> TDecimal.NumberBitsEncoding - 2; TSignificand significand; int exponent; - if (g0g1Bits == TValue.CreateTruncating(3)) + if ((value & TDecimal.G0G1Mask) == TDecimal.G0G1Mask) { - exponent = TDecimal.ConvertToExponent((value << 3) >> TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent); - significand = TDecimal.ConvertToSignificand((value << TDecimal.NumberBitsEncoding + 3) >> TDecimal.NumberBitsEncoding + 3); - significand += TDecimal.TwoPowerMostSignificantBitNumberOfSignificand; + exponent = TDecimal.ConvertToExponent((value & TDecimal.G2ToGwPlus3ExponentMask) >> (TDecimal.NumberBitsSignificand + 1)); + significand = TDecimal.ConvertToSignificand((value & TDecimal.GwPlus4SignificandMask) | TDecimal.MostSignificantBitOfSignificandMask); } else { - exponent = TDecimal.ConvertToExponent((value << 1) >> TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent); - significand = TDecimal.ConvertToSignificand((value << TDecimal.NumberBitsExponent + 1) >> TDecimal.NumberBitsExponent + 1); + exponent = TDecimal.ConvertToExponent((value & TDecimal.G0ToGwPlus1ExponentMask) >> (TDecimal.NumberBitsSignificand + 3)); + significand = TDecimal.ConvertToSignificand(value & TDecimal.GwPlus2ToGwPlus4SignificandMask); } return new DecimalIeee754(signed, exponent - TDecimal.Bias, significand); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index e3c373cbea2f5..70b3a66c1809b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -164,16 +164,23 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static UInt128 IDecimalIeee754UnpackInfo.SignMask => new UInt128(0x8000_0000_0000_0000, 0); - static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 128; + static int IDecimalIeee754UnpackInfo.Bias => Bias; - static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; + static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; - static int IDecimalIeee754UnpackInfo.Bias => Bias; + static UInt128 IDecimalIeee754UnpackInfo.G0G1Mask => new UInt128(0x6000_0000_0000_0000, 0); - static Int128 IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => new Int128(0x0002_0000_0000_0000, 0); + static UInt128 IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => new UInt128(0x7FFE_0000_0000_0000, 0); - static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; + 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 => new UInt128(0x0002_0000_0000_0000, 0); + + static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 110; private static Int128[] Int128Powers10 => [ @@ -213,9 +220,12 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin 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 index da883e28fa455..ef188439418a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -168,14 +168,8 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static uint IDecimalIeee754UnpackInfo.SignMask => 0x8000_0000; - static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 32; - - static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; - static int IDecimalIeee754UnpackInfo.Bias => Bias; - static int IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 8388608; - static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; static int IDecimalIeee754UnpackInfo.ConvertToExponent(uint value) => (int)value; @@ -184,6 +178,20 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754UnpackInfo.Power10(int exponent) => Int32Powers10[exponent]; + static uint IDecimalIeee754UnpackInfo.G0G1Mask => 0x60000000; + + static uint IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7f800000; + + static uint IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => 0x1fe00000; + + static uint IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => 0x007fffff; + + static uint IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => 0x001fffff; + + static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 20; + + static uint IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => 0x00800000; + static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal32NumberBufferLength; static bool IDecimalIeee754TryParseInfo.TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out int significand, out int 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 index 24e83ea497347..7892ed9eacb01 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -182,19 +182,28 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin 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 => 0x8000_0000_0000_0000; - static int IDecimalIeee754UnpackInfo.NumberBitsEncoding => 64; + static int IDecimalIeee754UnpackInfo.Bias => Bias; - static int IDecimalIeee754UnpackInfo.NumberBitsExponent => NumberBitsExponent; + static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; - static int IDecimalIeee754UnpackInfo.Bias => Bias; + static ulong IDecimalIeee754UnpackInfo.G0G1Mask => 0x6000_0000_0000_0000; - static long IDecimalIeee754UnpackInfo.TwoPowerMostSignificantBitNumberOfSignificand => 9_007_199_254_740_992; + static ulong IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7FE0_0000_0000_0000; - static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; + 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 => 0x0020_0000_0000_0000; static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal64NumberBufferLength; From ede6ca39c57fabc775d9691c88d89537452ecfb0 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 10 Apr 2024 18:05:50 +0700 Subject: [PATCH 11/23] Extract common mask to property. --- .../src/System/Number.DecimalIeee754.cs | 24 +++++++-------- .../src/System/Numerics/Decimal128.cs | 17 +++++++---- .../src/System/Numerics/Decimal32.cs | 29 ++++++++++++------- .../src/System/Numerics/Decimal64.cs | 23 ++++++++++----- 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 85012d2834d1b..9099ec173499e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -17,8 +17,10 @@ internal interface IDecimalIeee754ConstructorInfo static abstract int Bias { get; } static abstract int CountDigits(TSignificand number); static abstract TSignificand Power10(int exponent); - static abstract int MostSignificantBitNumberOfSignificand { get; } 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; } @@ -123,32 +125,30 @@ internal static TValue CalDecimalIeee754(TSignif } exponent += TDecimal.Bias; - bool msbSignificand = (unsignedSignificand & TSignificand.One << TDecimal.MostSignificantBitNumberOfSignificand) != TSignificand.Zero; 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 = TValue.One << TDecimal.NumberBitsEncoding - 1; + value = TDecimal.SignMask; } if (msbSignificand) { - value ^= TValue.One << TDecimal.NumberBitsEncoding - 2; - value ^= TValue.One << TDecimal.NumberBitsEncoding - 3; - exponentVal <<= TDecimal.NumberBitsEncoding - 4; - value ^= exponentVal; - significandVal <<= TDecimal.NumberBitsEncoding - TDecimal.MostSignificantBitNumberOfSignificand; - significandVal >>= TDecimal.NumberBitsCombinationField; - value ^= significandVal; + 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; + value |= exponentVal; + value |= significandVal; } return 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 index 70b3a66c1809b..f9ba802ffdb24 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -29,6 +29,9 @@ public readonly struct Decimal128 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) { @@ -142,8 +145,6 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754ConstructorInfo.Bias => Bias; - static int IDecimalIeee754ConstructorInfo.MostSignificantBitNumberOfSignificand => 113; - static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 128; static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 17; @@ -156,19 +157,25 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static UInt128 IDecimalIeee754ConstructorInfo.Zero => ZeroValue; + static UInt128 IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask; + + static UInt128 IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; + + static UInt128 IDecimalIeee754ConstructorInfo.SignMask => SignMask; + 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 => new UInt128(0x8000_0000_0000_0000, 0); + static UInt128 IDecimalIeee754UnpackInfo.SignMask => SignMask; static int IDecimalIeee754UnpackInfo.Bias => Bias; static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; - static UInt128 IDecimalIeee754UnpackInfo.G0G1Mask => new UInt128(0x6000_0000_0000_0000, 0); + static UInt128 IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask; static UInt128 IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => new UInt128(0x7FFE_0000_0000_0000, 0); @@ -178,7 +185,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static UInt128 IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => new UInt128(0x0000_7FFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); - static UInt128 IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => new UInt128(0x0002_0000_0000_0000, 0); + static UInt128 IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 110; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index ef188439418a6..d547fed8abea3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -24,10 +24,13 @@ public readonly struct Decimal32 private const int NumberDigitsPrecision = 7; private const int Bias = 101; private const int NumberBitsExponent = 8; - private const uint PositiveInfinityValue = 0x78000000; - private const uint NegativeInfinityValue = 0xf8000000; + 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) { @@ -152,8 +155,6 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754ConstructorInfo.Bias => Bias; - static int IDecimalIeee754ConstructorInfo.MostSignificantBitNumberOfSignificand => 23; - static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 32; static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 11; @@ -166,7 +167,13 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static uint IDecimalIeee754ConstructorInfo.Zero => ZeroValue; - static uint IDecimalIeee754UnpackInfo.SignMask => 0x8000_0000; + static uint IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask; + + static uint IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; + + static uint IDecimalIeee754ConstructorInfo.SignMask => SignMask; + + static uint IDecimalIeee754UnpackInfo.SignMask => SignMask; static int IDecimalIeee754UnpackInfo.Bias => Bias; @@ -178,19 +185,19 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754UnpackInfo.Power10(int exponent) => Int32Powers10[exponent]; - static uint IDecimalIeee754UnpackInfo.G0G1Mask => 0x60000000; + static uint IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask; - static uint IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7f800000; + static uint IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7F80_0000; - static uint IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => 0x1fe00000; + static uint IDecimalIeee754UnpackInfo.G2ToGwPlus3ExponentMask => 0x1FE0_0000; - static uint IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => 0x007fffff; + static uint IDecimalIeee754UnpackInfo.GwPlus2ToGwPlus4SignificandMask => 0x007F_FFFF; - static uint IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => 0x001fffff; + static uint IDecimalIeee754UnpackInfo.GwPlus4SignificandMask => 0x001F_FFFF; static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 20; - static uint IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => 0x00800000; + static uint IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal32NumberBufferLength; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index 7892ed9eacb01..917a1b9142be8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -22,10 +22,13 @@ public readonly struct Decimal64 private const int NumberDigitsPrecision = 16; private const int Bias = 398; private const int NumberBitsExponent = 10; - private const ulong PositiveInfinityValue = 0x7800000000000000; - private const ulong NegativeInfinityValue = 0xf800000000000000; - private const ulong ZeroValue = 0x0000000000000000; + 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) { @@ -165,8 +168,6 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754ConstructorInfo.Bias => Bias; - static int IDecimalIeee754ConstructorInfo.MostSignificantBitNumberOfSignificand => 53; - static int IDecimalIeee754ConstructorInfo.NumberBitsEncoding => 64; static int IDecimalIeee754ConstructorInfo.NumberBitsCombinationField => 13; @@ -179,19 +180,25 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static ulong IDecimalIeee754ConstructorInfo.Zero => ZeroValue; + static ulong IDecimalIeee754ConstructorInfo.G0G1Mask => G0G1Mask; + + static ulong IDecimalIeee754ConstructorInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; + + static ulong IDecimalIeee754ConstructorInfo.SignMask => SignMask; + 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 => 0x8000_0000_0000_0000; + static ulong IDecimalIeee754UnpackInfo.SignMask => SignMask; static int IDecimalIeee754UnpackInfo.Bias => Bias; static int IDecimalIeee754UnpackInfo.NumberDigitsPrecision => NumberDigitsPrecision; - static ulong IDecimalIeee754UnpackInfo.G0G1Mask => 0x6000_0000_0000_0000; + static ulong IDecimalIeee754UnpackInfo.G0G1Mask => G0G1Mask; static ulong IDecimalIeee754UnpackInfo.G0ToGwPlus1ExponentMask => 0x7FE0_0000_0000_0000; @@ -203,7 +210,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static int IDecimalIeee754UnpackInfo.NumberBitsSignificand => 50; - static ulong IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => 0x0020_0000_0000_0000; + static ulong IDecimalIeee754UnpackInfo.MostSignificantBitOfSignificandMask => MostSignificantBitOfSignificandMask; static int IDecimalIeee754TryParseInfo.DecimalNumberBufferLength => Number.Decimal64NumberBufferLength; From b4e5d3455fc05a0dc51697b0c69a1d5a3230e6f9 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 10 Apr 2024 18:36:58 +0700 Subject: [PATCH 12/23] add more tests --- .../tests/System.Runtime.Tests/System/Decimal128Tests.cs | 2 ++ .../tests/System.Runtime.Tests/System/Decimal32Tests.cs | 2 ++ .../tests/System.Runtime.Tests/System/Decimal64Tests.cs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs index 11106dbe27a75..1544b4f1b317b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs @@ -242,6 +242,8 @@ public static void CompareTo_Other_ReturnsExpected() Assert.Equal(d1, d2); } Assert.Equal(new Decimal128(-1, 1), new Decimal128(-10, 0)); + Assert.Equal(new Decimal128(1, 6144), new Decimal128(10, 6143)); + Assert.Equal(new Decimal128(Int128.Parse(new string('9', 33)), 6111), new Decimal128(Int128.Parse(new string('9', 33) + "0"), 6110)); Assert.NotEqual(new Decimal128(1, 1), new Decimal128(-10, 0)); Assert.NotEqual(new Decimal128(-1, 1), new Decimal128(10, 0)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs index 4de487be22a7d..3a846c7cf4de5 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -241,6 +241,8 @@ public static void CompareTo_Other_ReturnsExpected() Assert.Equal(d1, d2); } Assert.Equal(new Decimal32(-1, 1), new Decimal32(-10, 0)); + Assert.Equal(new Decimal32(1, 90), new Decimal32(10, 89)); + Assert.Equal(new Decimal32(999999, 90), new Decimal32(9999990, 89)); Assert.NotEqual(new Decimal32(1, 1), new Decimal32(-10, 0)); Assert.NotEqual(new Decimal32(-1, 1), new Decimal32(10, 0)); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index d8a6cd14aeb0e..7ea775b44713d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -242,6 +242,8 @@ public static void CompareTo_Other_ReturnsExpected() Assert.Equal(d1, d2); } Assert.Equal(new Decimal64(-1, 1), new Decimal64(-10, 0)); + Assert.Equal(new Decimal64(1, 369), new Decimal64(10, 368)); + Assert.Equal(new Decimal64(long.Parse(new string('9', 15)), 369), new Decimal64(long.Parse(new string('9', 15) + "0"), 368)); Assert.NotEqual(new Decimal64(1, 1), new Decimal64(-10, 0)); Assert.NotEqual(new Decimal64(-1, 1), new Decimal64(10, 0)); } From 504f042f7d197f2126c572e773ffef2f99cce7bc Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 11 Apr 2024 13:21:22 +0700 Subject: [PATCH 13/23] add comments --- .../src/System/Numerics/Decimal128.cs | 6 ++++++ .../src/System/Numerics/Decimal32.cs | 9 +++++++++ .../src/System/Numerics/Decimal64.cs | 8 +++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index f9ba802ffdb24..fb2dcea7b2f5f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -80,6 +80,12 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I 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) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index d547fed8abea3..53a689ec80f87 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -91,6 +91,15 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I 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) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index 917a1b9142be8..dacc149273a3f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -103,6 +103,12 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I 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) @@ -112,7 +118,7 @@ public int CompareTo(object? value) if (value is not Decimal64 i) { - throw new ArgumentException(SR.Arg_MustBeDecimal32); + throw new ArgumentException(SR.Arg_MustBeDecimal64); } return Number.CompareDecimalIeee754(_value, i._value); From 31ffd5cd47ca95ddccd170e949fd6caae323acbe Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 11 Apr 2024 13:43:42 +0700 Subject: [PATCH 14/23] Add overflow message. --- .../src/System/Number.DecimalIeee754.cs | 7 ++++--- .../src/System/Numerics/Decimal128.cs | 2 ++ .../src/System/Numerics/Decimal32.cs | 2 ++ .../src/System/Numerics/Decimal64.cs | 2 ++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 9099ec173499e..31b74e55bfd21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -26,6 +26,7 @@ internal interface IDecimalIeee754ConstructorInfo static abstract TValue PositiveInfinityBits { get; } static abstract TValue NegativeInfinityBits { get; } static abstract TValue Zero { get; } + static abstract string OverflowMessage { get; } } internal interface IDecimalIeee754UnpackInfo @@ -77,7 +78,7 @@ internal static TValue CalDecimalIeee754(TSignif } if (exponent < TDecimal.MinDecimalExponent) { - throw new OverflowException(SR.Overflow_Decimal); + throw new OverflowException(TDecimal.OverflowMessage); } } @@ -87,7 +88,7 @@ internal static TValue CalDecimalIeee754(TSignif if (exponent + numberDigitsRemoving > TDecimal.MaxDecimalExponent) { - throw new OverflowException(SR.Overflow_Decimal); + throw new OverflowException(TDecimal.OverflowMessage); } exponent += numberDigitsRemoving; @@ -118,7 +119,7 @@ internal static TValue CalDecimalIeee754(TSignif if (numberSignificandDigits + numberZeroDigits > TDecimal.NumberDigitsPrecision) { - throw new OverflowException(SR.Overflow_Decimal); + throw new OverflowException(TDecimal.OverflowMessage); } unsignedSignificand *= TDecimal.Power10(numberZeroDigits); exponent -= numberZeroDigits; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index fb2dcea7b2f5f..530e5ee91a77c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -169,6 +169,8 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 53a689ec80f87..ee21f844a465c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -182,6 +182,8 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static uint IDecimalIeee754ConstructorInfo.SignMask => SignMask; + static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal32; + static uint IDecimalIeee754UnpackInfo.SignMask => SignMask; static int IDecimalIeee754UnpackInfo.Bias => Bias; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index dacc149273a3f..3ce9eafdf4448 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -192,6 +192,8 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin 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; From 6212873f468f91f6ac15d94af529e1375f8ea66f Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 11 Apr 2024 13:44:07 +0700 Subject: [PATCH 15/23] Add overflow message --- .../System.Private.CoreLib/src/System/Numerics/Decimal32.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index ee21f844a465c..93608bcde2441 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -182,7 +182,7 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin static uint IDecimalIeee754ConstructorInfo.SignMask => SignMask; - static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal32; + static string IDecimalIeee754ConstructorInfo.OverflowMessage => SR.Overflow_Decimal32; static uint IDecimalIeee754UnpackInfo.SignMask => SignMask; From cb1d8b11f8e6bd25017ff95bc4bdc4592a561d6a Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 11 Apr 2024 17:16:31 +0700 Subject: [PATCH 16/23] split uint128 to ulong. --- .../src/System/Numerics/Decimal128.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index 530e5ee91a77c..ea48de3cac15e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -18,7 +18,13 @@ public readonly struct Decimal128 IDecimalIeee754UnpackInfo, IDecimalIeee754TryParseInfo { - internal readonly UInt128 _value; +#if BIGENDIAN + private readonly ulong _upper; + private readonly ulong _lower; +#else + private readonly ulong _lower; + private readonly ulong _upper; +#endif private const int MaxDecimalExponent = 6111; private const int MinDecimalExponent = -6176; @@ -35,7 +41,9 @@ public readonly struct Decimal128 public Decimal128(Int128 significand, int exponent) { - _value = Number.CalDecimalIeee754(significand, 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); @@ -98,12 +106,17 @@ public int CompareTo(object? value) throw new ArgumentException(SR.Arg_MustBeDecimal128); } - return Number.CompareDecimalIeee754(_value, i._value); + var current = new UInt128(_upper, _lower); + var other = new UInt128(i._upper, i._lower); + + return Number.CompareDecimalIeee754(current, other); } public bool Equals(Decimal128 other) { - return Number.CompareDecimalIeee754(_value, other._value) == 0; + 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) @@ -113,7 +126,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { - return _value.GetHashCode(); + return new UInt128(_upper, _lower).GetHashCode(); } public override string ToString() From 0a4620e9fb38311998d28c3c50ad21f720aebeff Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 11 Apr 2024 17:31:07 +0700 Subject: [PATCH 17/23] Change private to internal --- .../src/System/Number.Formatting.cs | 2 +- .../src/System/Numerics/Decimal128.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 98a4b23377589..3d78108aaa303 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -530,7 +530,7 @@ internal static unsafe void Decimal64ToNumber(Decimal64 d, ref NumberBuffer numb internal static unsafe void Decimal128ToNumber(Decimal128 d, ref NumberBuffer number) { byte* buffer = number.DigitsPtr; - DecimalIeee754 decimal128 = UnpackDecimalIeee754(d._value); + DecimalIeee754 decimal128 = UnpackDecimalIeee754(new UInt128(d._upper, d._lower)); number.IsNegative = decimal128.Signed; byte* p = buffer + Decimal128Precision; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index ea48de3cac15e..2ec5755647fc0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -19,11 +19,11 @@ public readonly struct Decimal128 IDecimalIeee754TryParseInfo { #if BIGENDIAN - private readonly ulong _upper; - private readonly ulong _lower; + internal readonly ulong _upper; + internal readonly ulong _lower; #else - private readonly ulong _lower; - private readonly ulong _upper; + internal readonly ulong _lower; + internal readonly ulong _upper; #endif private const int MaxDecimalExponent = 6111; From 8f3d4ff072ec679964e54a5bb8fcafa16c8fa6d4 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Fri, 12 Apr 2024 15:51:34 +0700 Subject: [PATCH 18/23] validate overflow decimal case. --- .../System.Private.CoreLib/src/System/Number.DecimalIeee754.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 31b74e55bfd21..705361d754e09 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -63,7 +63,8 @@ internal static TValue CalDecimalIeee754(TSignif TSignificand unsignedSignificand = significand > TSignificand.Zero ? significand : -significand; - if (unsignedSignificand > TDecimal.MaxSignificand && exponent > TDecimal.MaxDecimalExponent) + if ((unsignedSignificand > TDecimal.MaxSignificand && exponent >= TDecimal.MaxDecimalExponent) + || (unsignedSignificand == TDecimal.MaxSignificand && exponent > TDecimal.MaxDecimalExponent)) { return significand > TSignificand.Zero ? TDecimal.PositiveInfinityBits : TDecimal.NegativeInfinityBits; } From db834cb1e58f68a12259b217ab874da1f0f17dd8 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Thu, 18 Apr 2024 19:45:59 +0700 Subject: [PATCH 19/23] add more tests --- .../src/System/Number.DecimalIeee754.cs | 6 +++--- .../tests/System.Runtime.Tests/System/Decimal128Tests.cs | 5 +++++ .../tests/System.Runtime.Tests/System/Decimal32Tests.cs | 5 +++++ .../tests/System.Runtime.Tests/System/Decimal64Tests.cs | 5 +++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 705361d754e09..73b382c2e5522 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -72,14 +72,14 @@ internal static TValue CalDecimalIeee754(TSignif TSignificand ten = TSignificand.CreateTruncating(10); if (exponent < TDecimal.MinDecimalExponent) { - while (unsignedSignificand >= ten) + while (unsignedSignificand > TSignificand.Zero && exponent < TDecimal.MinDecimalExponent) { unsignedSignificand /= ten; ++exponent; } - if (exponent < TDecimal.MinDecimalExponent) + if (unsignedSignificand == TSignificand.Zero) { - throw new OverflowException(TDecimal.OverflowMessage); + return TDecimal.Zero; } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs index 1544b4f1b317b..04e97e1feb5a0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs @@ -42,6 +42,11 @@ public static IEnumerable Parse_Valid_TestData() yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal128(-56789, -2) }; yield return new object[] { "0.666666666666666666666666666666666650000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal128(Int128.Parse(new string('6', 34)), -34) }; + yield return new object[] { "0." + new string('0', 6176) + "1", defaultStyle, invariantFormat, new Decimal128(0, 0) }; + yield return new object[] { "-0." + new string('0', 6176) + "1", defaultStyle, invariantFormat, new Decimal128(0, 0) }; + yield return new object[] { "0." + new string('0', 6175) + "1", defaultStyle, invariantFormat, new Decimal128(1, -6176) }; + yield return new object[] { "-0." + new string('0', 6175) + "1", defaultStyle, invariantFormat, new Decimal128(-1, -6176) }; + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal128(234, -3) }; yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal128(234, 0) }; yield return new object[] { "7" + new string('0', 6144) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal128(7, 6144) }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs index 3a846c7cf4de5..b4235cd32a146 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -42,6 +42,11 @@ public static IEnumerable Parse_Valid_TestData() yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal32(-56789, -2) }; yield return new object[] { "0.6666666500000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal32(6666666, -7) }; + yield return new object[] { "0." + new string('0', 101) + "1", defaultStyle, invariantFormat, new Decimal32(0, 0) }; + yield return new object[] { "-0." + new string('0', 101) + "1", defaultStyle, invariantFormat, new Decimal32(0, 0) }; + yield return new object[] { "0." + new string('0', 100) + "1", defaultStyle, invariantFormat, new Decimal32(1, -101) }; + yield return new object[] { "-0." + new string('0', 100) + "1", defaultStyle, invariantFormat, new Decimal32(-1, -101) }; + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal32(234, -3) }; yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal32(234, 0) }; yield return new object[] { "7" + new string('0', 96) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal32(7, 96) }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index 7ea775b44713d..180a11d4c8b3b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -42,6 +42,11 @@ public static IEnumerable Parse_Valid_TestData() yield return new object[] { (-567.89).ToString(), defaultStyle, null, new Decimal64(-56789, -2) }; yield return new object[] { "0.6666666666666666500000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, new Decimal64(6666666666666666, -16) }; + yield return new object[] { "0." + new string('0', 398) + "1", defaultStyle, invariantFormat, new Decimal64(0, 0) }; + yield return new object[] { "-0." + new string('0', 398) + "1", defaultStyle, invariantFormat, new Decimal64(0, 0) }; + yield return new object[] { "0." + new string('0', 397) + "1", defaultStyle, invariantFormat, new Decimal64(1, -398) }; + yield return new object[] { "-0." + new string('0', 397) + "1", defaultStyle, invariantFormat, new Decimal64(-1, -398) }; + yield return new object[] { emptyFormat.NumberDecimalSeparator + "234", defaultStyle, null, new Decimal64(234, -3) }; yield return new object[] { "234" + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal64(234, 0) }; yield return new object[] { "7" + new string('0', 384) + emptyFormat.NumberDecimalSeparator, defaultStyle, null, new Decimal64(7, 384) }; From 2ed126b4cf4d52a7fab354ae4eb9d7d009327cd7 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 29 May 2024 14:54:47 +0700 Subject: [PATCH 20/23] add more tests --- .../src/System/Numerics/Decimal128.cs | 8 ++++++++ .../src/System/Numerics/Decimal32.cs | 6 ++++++ .../src/System/Numerics/Decimal64.cs | 6 ++++++ src/libraries/System.Runtime/ref/System.Runtime.cs | 6 ++++++ .../System.Runtime.Tests/System/Decimal128Tests.cs | 13 +++++++++++++ .../System.Runtime.Tests/System/Decimal32Tests.cs | 13 +++++++++++++ .../System.Runtime.Tests/System/Decimal64Tests.cs | 13 +++++++++++++ 7 files changed, 65 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index 2ec5755647fc0..bca1099cfe10c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -12,6 +12,7 @@ namespace System.Numerics { public readonly struct Decimal128 : IComparable, + IComparable, IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, @@ -112,6 +113,13 @@ public int CompareTo(object? value) 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); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 93608bcde2441..31bd75f6c8f35 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -11,6 +11,7 @@ namespace System.Numerics [StructLayout(LayoutKind.Sequential)] public readonly struct Decimal32 : IComparable, + IComparable, IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, @@ -115,6 +116,11 @@ public int CompareTo(object? value) 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index 3ce9eafdf4448..215b7fb79bb71 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -9,6 +9,7 @@ namespace System.Numerics { public readonly struct Decimal64 : IComparable, + IComparable, IEquatable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, @@ -124,6 +125,11 @@ public int CompareTo(object? value) 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; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 0c99b61663219..be29294503a6c 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10615,11 +10615,13 @@ namespace System.Numerics { public readonly struct Decimal32 : System.IComparable, + System.IComparable, System.IEquatable { 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; } @@ -10644,11 +10646,13 @@ public readonly struct Decimal32 public readonly struct Decimal64 : System.IComparable, + System.IComparable, System.IEquatable { public Decimal64(long significand, int exponent) { throw null; } public int CompareTo(object? value) { throw null; } + public int CompareTo(Decimal64 other) { throw null; } public bool Equals(Decimal64 other) { throw null; } public static Decimal64 Parse(string s) { throw null; } @@ -10674,11 +10678,13 @@ public readonly struct Decimal64 public readonly struct Decimal128 : System.IComparable, + System.IComparable, System.IEquatable { public Decimal128(Int128 significand, int exponent) { throw null; } public int CompareTo(object? value) { throw null; } + public int CompareTo(Decimal128 other) { throw null; } public bool Equals(Decimal128 other) { throw null; } public static Decimal128 Parse(string s) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs index 04e97e1feb5a0..5f0a764511c4c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal128Tests.cs @@ -253,6 +253,19 @@ public static void CompareTo_Other_ReturnsExpected() Assert.NotEqual(new Decimal128(-1, 1), new Decimal128(10, 0)); } + [Fact] + public static void CompareToTest() + { + var d1 = new Decimal128(-1, 1); + var d2 = new Decimal128(-10, 0); + Assert.Equal(0, d1.CompareTo(d2)); + + d1 = new Decimal128(1, 1); + d2 = new Decimal128(-1, 0); + Assert.Equal(1, d1.CompareTo(d2)); + Assert.Equal(-1, d2.CompareTo(d1)); + } + [Fact] public static void GetHashCodeTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs index b4235cd32a146..e3ad15422c4fd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal32Tests.cs @@ -252,6 +252,19 @@ public static void CompareTo_Other_ReturnsExpected() Assert.NotEqual(new Decimal32(-1, 1), new Decimal32(10, 0)); } + [Fact] + public static void CompareToTest() + { + var d1 = new Decimal32(-1, 1); + var d2 = new Decimal32(-10, 0); + Assert.Equal(0, d1.CompareTo(d2)); + + d1 = new Decimal32(1, 1); + d2 = new Decimal32(-1, 0); + Assert.Equal(1, d1.CompareTo(d2)); + Assert.Equal(-1, d2.CompareTo(d1)); + } + [Fact] public static void GetHashCodeTest() { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index 180a11d4c8b3b..d84e53352839c 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs @@ -253,6 +253,19 @@ public static void CompareTo_Other_ReturnsExpected() Assert.NotEqual(new Decimal64(-1, 1), new Decimal64(10, 0)); } + [Fact] + public static void CompareToTest() + { + var d1 = new Decimal64(-1, 1); + var d2 = new Decimal64(-10, 0); + Assert.Equal(0, d1.CompareTo(d2)); + + d1 = new Decimal64(1, 1); + d2 = new Decimal64(-1, 0); + Assert.Equal(1, d1.CompareTo(d2)); + Assert.Equal(-1, d2.CompareTo(d1)); + } + [Fact] public static void GetHashCodeTest() { From d08b7b565cc2f03ba2d63f3949cfde1a4c83f4e0 Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Wed, 29 May 2024 17:44:01 +0700 Subject: [PATCH 21/23] add more tests --- .../System.Private.CoreLib/src/System/Number.Parsing.cs | 3 +-- .../src/System/Numerics/Decimal128.cs | 1 + .../src/System/Numerics/Decimal32.cs | 1 + .../src/System/Numerics/Decimal64.cs | 1 + src/libraries/System.Runtime/ref/System.Runtime.cs | 9 ++++++--- 5 files changed, 10 insertions(+), 5 deletions(-) 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 1bb9ebb279b2c..8a53bf92c88b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Unicode; -using static System.Number; namespace System { @@ -100,7 +99,7 @@ internal interface IDecimalIeee754TryParseInfo where TSignificand : unmanaged, IBinaryInteger { static abstract int DecimalNumberBufferLength { get; } - static abstract bool TryNumberToDecimalIeee754(ref NumberBuffer number, out TSignificand significand, out int exponent); + static abstract bool TryNumberToDecimalIeee754(ref Number.NumberBuffer number, out TSignificand significand, out int exponent); static abstract TSelf Construct(TSignificand significand, int exponent); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs index bca1099cfe10c..3c487c8291205 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -14,6 +14,7 @@ public readonly struct Decimal128 : IComparable, IComparable, IEquatable, + ISpanParsable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, IDecimalIeee754UnpackInfo, diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs index 31bd75f6c8f35..551c61c3e85ea 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -13,6 +13,7 @@ public readonly struct Decimal32 : IComparable, IComparable, IEquatable, + ISpanParsable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, IDecimalIeee754UnpackInfo, diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs index 215b7fb79bb71..79e7560e39272 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -11,6 +11,7 @@ public readonly struct Decimal64 : IComparable, IComparable, IEquatable, + ISpanParsable, IDecimalIeee754ParseAndFormatInfo, IDecimalIeee754ConstructorInfo, IDecimalIeee754UnpackInfo, diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index be29294503a6c..b0bea35236e59 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10616,7 +10616,8 @@ namespace System.Numerics public readonly struct Decimal32 : System.IComparable, System.IComparable, - System.IEquatable + System.IEquatable, + System.ISpanParsable { public Decimal32(int significand, int exponent) { throw null; } @@ -10647,7 +10648,8 @@ public readonly struct Decimal32 public readonly struct Decimal64 : System.IComparable, System.IComparable, - System.IEquatable + System.IEquatable, + System.ISpanParsable { public Decimal64(long significand, int exponent) { throw null; } @@ -10679,7 +10681,8 @@ public readonly struct Decimal64 public readonly struct Decimal128 : System.IComparable, System.IComparable, - System.IEquatable + System.IEquatable, + System.ISpanParsable { public Decimal128(Int128 significand, int exponent) { throw null; } From 49f66765c7ba3167038f10a4a3b644b9d496fc44 Mon Sep 17 00:00:00 2001 From: RaymondHuy Date: Tue, 17 Dec 2024 09:33:43 +0700 Subject: [PATCH 22/23] Correct naming Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../System.Private.CoreLib/src/System/Number.DecimalIeee754.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 73b382c2e5522..5a53f1d86c56f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -97,7 +97,7 @@ internal static TValue CalDecimalIeee754(TSignif TSignificand quotient = unsignedSignificand / divisor; TSignificand remainder = unsignedSignificand % divisor; TSignificand midPoint = divisor >> 1; - bool needRouding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One); + bool needRounding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One); if (needRouding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent) { From f82d9a64c7a269b7c96da1107b18f1196c1b8cbc Mon Sep 17 00:00:00 2001 From: raymondhuy177 Date: Tue, 17 Dec 2024 10:28:02 +0700 Subject: [PATCH 23/23] fix naming convention --- .../src/System/Number.DecimalIeee754.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs index 5a53f1d86c56f..790773743e85c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -99,12 +99,12 @@ internal static TValue CalDecimalIeee754(TSignif TSignificand midPoint = divisor >> 1; bool needRounding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One); - if (needRouding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent) + if (needRounding && quotient == TDecimal.MaxSignificand && exponent < TDecimal.MaxDecimalExponent) { unsignedSignificand = TDecimal.Power10(TDecimal.NumberDigitsPrecision - 1); exponent++; } - else if (needRouding && quotient < TDecimal.MaxSignificand) + else if (needRounding && quotient < TDecimal.MaxSignificand) { unsignedSignificand = quotient + TSignificand.One; }