-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Add Decimal32, Decimal64, Decimal128 #100729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Note regarding the |
src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
Outdated
Show resolved
Hide resolved
| static int IDecimalIeee754UnpackInfo<Decimal128, Int128, UInt128>.NumberDigitsPrecision => NumberDigitsPrecision; | ||
|
|
||
|
|
||
| private static Int128[] Int128Powers10 => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If Int128 is not optimized, this can be optimally stored as a ReadOnlySpan of Int32/Int64 like Number.BigInteger.Pow10BigNumTable
| public override int GetHashCode() | ||
| { | ||
| return _value.GetHashCode(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+0 and -0 should have the same hash code. The easiest way can be stripping the sign bit.
I also remember that there can be same values with different representations.
runtime/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs
Lines 125 to 132 in 9b57a26
| else | |
| { | |
| // Equivalant values are compared by their exponent parts, | |
| // and the value with smaller exponent is considered closer to zero. | |
| // This only applies to IEEE 754 decimals. Consider to add support if decimals are added into .NET. | |
| return 0; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@huoyaoyuan Thanks for your review. However this PR targets only the first phase as following: #81376 (comment) so I haven't added NegativeZero and PositiveZero. I will update GetHashCode method in the next phase.
src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs
Outdated
Show resolved
Hide resolved
|
This is on my radar to take a look at; just noting it might be a bit delayed due to other priorities for the .NET 9 release. CC. @jeffhandley |
| } | ||
| } | ||
|
|
||
| private static unsafe TValue DecimalIeee754BinaryEncoding<TDecimal, TValue>(bool signed, TValue significand, int exponent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: exponent and significand are swapped compared to the typical order of such APIs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These types of APIs, even though they are private/internal, likely also benefit from XML doc comments to be explicit that that significand is the "full" (non-trailing) significand and exponent is the unbiased exponent.
The terms get interchanged a lot and so being explicit can help ensure future readers understand the intent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have updated the method name and added comments to help it readable.
| byte[] buffer = ArrayPool<byte>.Shared.Rent(TDecimal.BufferLength); | ||
| try | ||
| { | ||
| fixed (byte* pDigits = buffer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more cases that are using unnecessary unsafe. NumberBuffer takes a span
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have updated it.
| char* stackPtr = stackalloc char[CharStackBufferSize]; | ||
| var vlb = new ValueListBuilder<char>(new Span<char>(stackPtr, CharStackBufferSize)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can directly use stackalloc in the ValueListBuilder constructor as well, since the language will convert it to span implicitly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @tannergooding , I follow your instruction, but it throws this error,

do you have another suggestion, or we just keep the old one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, unfortunately there's actually a missing language feature here due to how we pass the data to NumberToString. We need ref scoped SomeRefStruct to do this one in particular.
Other callouts may not need that feature, just this one in particular does.
| finally | ||
| { | ||
| ArrayPool<byte>.Shared.Return(buffer); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't typically put the return in a finally block. An exception being thrown is very unexpected and returning is not required for correctness
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have removed the finally block.
| where TValue : unmanaged, IBinaryInteger<TValue> | ||
| { | ||
| byte* buffer = number.DigitsPtr; | ||
| DecimalIeee754<TValue> unpackDecimal = value.Unpack(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unpack should likely not be an extension method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @tannergooding , it is not an extension method, it is a method declared in IDecimalIeee754ParseAndFormatInfo interface
| DecimalIeee754<TValue> unpackDecimal = value.Unpack(); | ||
| number.IsNegative = unpackDecimal.Signed; | ||
|
|
||
| byte* p = buffer + TDecimal.Precision; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More cases that should use Span instead. There's a number of them and I don't plan to comment every instance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have updated it.
| private static readonly UInt128 PositiveInfinityValue = new UInt128(upper: 0x7800_0000_0000_0000, lower: 0); | ||
| private static readonly UInt128 NegativeInfinityValue = new UInt128(upper: 0xf800_0000_0000_0000, lower: 0); | ||
| private static readonly UInt128 ZeroValue = new UInt128(0, 0); | ||
| private static readonly UInt128 NegativeZeroValue = new UInt128(0x8000_0000_0000_0000, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be a problem for codegen since UInt128 is not const?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They should probably be properties instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I have updated it.
| return Number.FormatDecimalIeee754<Decimal128, UInt128>(this, format, NumberFormatInfo.GetInstance(provider)); | ||
| } | ||
|
|
||
| private static UInt128[] UInt128Powers10 => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be a problem for constant embedding. long array can be encoded into executable and read by pairs.
|
Are there any preliminary benchmarks of these new types? I.e. How do Also, can we expect these new types to arrive in |
We are focusing on the different semantic first. Both types are having different existing standard to follow.
It's optimistic, but not a guarantee. |
|
Hi @tannergooding, thanks for your review, I have resolved almost your comments (handle NaN, |
Resolve #81376