Skip to content
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

Add Decimal32, Decimal64, Decimal128 #100729

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libraries/Common/src/System/Number.NumberBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ internal static partial class Number
internal const int UInt32NumberBufferLength = 10 + 1; // 10 for the longest input: 4,294,967,295
internal const int UInt64NumberBufferLength = 20 + 1; // 20 for the longest input: 18,446,744,073,709,551,615
internal const int UInt128NumberBufferLength = 39 + 1; // 39 for the longest input: 340,282,366,920,938,463,463,374,607,431,768,211,455
internal const int Decimal32NumberBufferLength = 97 + 1 + 1; // 97 for the longest input + 1 for rounding
internal const int Decimal64NumberBufferLength = 385 + 1 + 1; // 385 for the longest input + 1 for rounding
internal const int Decimal128NumberBufferLength = 6145 + 1 + 1; // 6145 for the longest input + 1 for rounding

internal unsafe ref struct NumberBuffer
{
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@
<data name="Arg_MustBeDecimal" xml:space="preserve">
<value>Object must be of type Decimal.</value>
</data>
<data name="Arg_MustBeDecimal32" xml:space="preserve">
<value>Object must be of type Decimal32.</value>
</data>
<data name="Arg_MustBeDecimal64" xml:space="preserve">
<value>Object must be of type Decimal64.</value>
</data>
<data name="Arg_MustBeDecimal128" xml:space="preserve">
<value>Object must be of type Decimal128.</value>
</data>
<data name="Arg_MustBeDelegate" xml:space="preserve">
<value>Type must derive from Delegate.</value>
</data>
Expand Down Expand Up @@ -3203,6 +3212,15 @@
<data name="Overflow_Decimal" xml:space="preserve">
<value>Value was either too large or too small for a Decimal.</value>
</data>
<data name="Overflow_Decimal32" xml:space="preserve">
<value>Value was either too large or too small for a Decimal32.</value>
</data>
<data name="Overflow_Decimal64" xml:space="preserve">
<value>Value was either too large or too small for a Decimal64.</value>
</data>
<data name="Overflow_Decimal128" xml:space="preserve">
<value>Value was either too large or too small for a Decimal128.</value>
</data>
<data name="Overflow_Duration" xml:space="preserve">
<value>The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@
<Compile Include="$(MSBuildThisFileDirectory)System\IFormatProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IFormattable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Index.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Number.DecimalIeee754.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Decimal128.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Decimal32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\Decimal64.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SearchValues\Any1CharPackedSearchValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SearchValues\Any1CharPackedIgnoreCaseSearchValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SearchValues\Any2CharPackedIgnoreCaseSearchValues.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;

namespace System
{
internal interface IDecimalIeee754ConstructorInfo<TSelf, TSignificand, TValue>
where TSelf : unmanaged, IDecimalIeee754ConstructorInfo<TSelf, TSignificand, TValue>
where TSignificand : IBinaryInteger<TSignificand>
where TValue : IBinaryInteger<TValue>
{
static abstract TSignificand MaxSignificand { get; }
static abstract int MaxDecimalExponent { get; }
static abstract int MinDecimalExponent { get; }
static abstract int NumberDigitsPrecision { get; }
static abstract int Bias { get; }
static abstract int CountDigits(TSignificand number);
static abstract TSignificand Power10(int exponent);
static abstract int NumberBitsEncoding { get; }
static abstract TValue G0G1Mask { get; }
static abstract TValue MostSignificantBitOfSignificandMask { get; }
static abstract TValue SignMask { get; }
static abstract int NumberBitsCombinationField { get; }
static abstract int NumberBitsExponent { get; }
static abstract TValue PositiveInfinityBits { get; }
static abstract TValue NegativeInfinityBits { get; }
static abstract TValue Zero { get; }
static abstract string OverflowMessage { get; }
}

internal interface IDecimalIeee754UnpackInfo<TSelf, TSignificand, TValue>
where TSelf : unmanaged, IDecimalIeee754UnpackInfo<TSelf, TSignificand, TValue>
where TSignificand : IBinaryInteger<TSignificand>
where TValue : IBinaryInteger<TValue>
{
static abstract TValue SignMask { get; }
static abstract TValue G0G1Mask { get; }
static abstract TValue G0ToGwPlus1ExponentMask { get; } //G0 to G(w+1)
static abstract TValue G2ToGwPlus3ExponentMask { get; } //G2 to G(w+3)
static abstract TValue GwPlus2ToGwPlus4SignificandMask { get; } //G(w+2) to G(w+4)
static abstract TValue GwPlus4SignificandMask { get; } //G(w+4)
static abstract int NumberBitsSignificand { get; }
static abstract int NumberDigitsPrecision { get; }
static abstract int Bias { get; }
static abstract TValue MostSignificantBitOfSignificandMask { get; }
static abstract int ConvertToExponent(TValue value);
static abstract TSignificand ConvertToSignificand(TValue value);
static abstract TSignificand Power10(int exponent);
}

internal static partial class Number
{
internal static TValue CalDecimalIeee754<TDecimal, TSignificand, TValue>(TSignificand significand, int exponent)
where TDecimal : unmanaged, IDecimalIeee754ConstructorInfo<TDecimal, TSignificand, TValue>
where TSignificand : IBinaryInteger<TSignificand>
where TValue : IBinaryInteger<TValue>
{
if (significand == TSignificand.Zero)
{
return TValue.Zero;
}

TSignificand unsignedSignificand = significand > TSignificand.Zero ? significand : -significand;

if ((unsignedSignificand > TDecimal.MaxSignificand && exponent >= TDecimal.MaxDecimalExponent)
|| (unsignedSignificand == TDecimal.MaxSignificand && exponent > TDecimal.MaxDecimalExponent))
{
return significand > TSignificand.Zero ? TDecimal.PositiveInfinityBits : TDecimal.NegativeInfinityBits;
}

TSignificand ten = TSignificand.CreateTruncating(10);
if (exponent < TDecimal.MinDecimalExponent)
{
while (unsignedSignificand > TSignificand.Zero && exponent < TDecimal.MinDecimalExponent)
{
unsignedSignificand /= ten;
++exponent;
}
if (unsignedSignificand == TSignificand.Zero)
{
return TDecimal.Zero;
}
}

if (unsignedSignificand > TDecimal.MaxSignificand)
{
int numberDigitsRemoving = TDecimal.CountDigits(unsignedSignificand) - TDecimal.NumberDigitsPrecision;

if (exponent + numberDigitsRemoving > TDecimal.MaxDecimalExponent)
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
{
throw new OverflowException(TDecimal.OverflowMessage);
}

exponent += numberDigitsRemoving;
TSignificand divisor = TDecimal.Power10(numberDigitsRemoving);
TSignificand quotient = unsignedSignificand / divisor;
TSignificand remainder = unsignedSignificand % divisor;
TSignificand midPoint = divisor >> 1;
bool needRouding = remainder > midPoint || (remainder == midPoint && (quotient & TSignificand.One) == TSignificand.One);
RaymondHuy marked this conversation as resolved.
Show resolved Hide resolved

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)
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
{
throw new OverflowException(TDecimal.OverflowMessage);
}
unsignedSignificand *= TDecimal.Power10(numberZeroDigits);
exponent -= numberZeroDigits;
}

exponent += TDecimal.Bias;

TValue value = TValue.Zero;
TValue exponentVal = TValue.CreateTruncating(exponent);
TValue significandVal = TValue.CreateTruncating(unsignedSignificand);
bool msbSignificand = (significandVal & TDecimal.MostSignificantBitOfSignificandMask) != TValue.Zero;

if (significand < TSignificand.Zero)
{
value = TDecimal.SignMask;
}

if (msbSignificand)
{
value |= TDecimal.G0G1Mask;
exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 3;
value |= exponentVal;
significandVal ^= TDecimal.MostSignificantBitOfSignificandMask;
value |= significandVal;
}
else
{
exponentVal <<= TDecimal.NumberBitsEncoding - TDecimal.NumberBitsExponent - 1;
value |= exponentVal;
value |= significandVal;
}

return value;
}

internal struct DecimalIeee754<TSignificand>
where TSignificand : IBinaryInteger<TSignificand>
{
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<TSignificand> UnpackDecimalIeee754<TDecimal, TSignificand, TValue>(TValue value)
where TDecimal : unmanaged, IDecimalIeee754UnpackInfo<TDecimal, TSignificand, TValue>
where TSignificand : IBinaryInteger<TSignificand>
where TValue : IBinaryInteger<TValue>
{
bool signed = (value & TDecimal.SignMask) != TValue.Zero;
TSignificand significand;
int exponent;

if ((value & TDecimal.G0G1Mask) == TDecimal.G0G1Mask)
{
exponent = TDecimal.ConvertToExponent((value & TDecimal.G2ToGwPlus3ExponentMask) >> (TDecimal.NumberBitsSignificand + 1));
significand = TDecimal.ConvertToSignificand((value & TDecimal.GwPlus4SignificandMask) | TDecimal.MostSignificantBitOfSignificandMask);
}
else
{
exponent = TDecimal.ConvertToExponent((value & TDecimal.G0ToGwPlus1ExponentMask) >> (TDecimal.NumberBitsSignificand + 3));
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
significand = TDecimal.ConvertToSignificand(value & TDecimal.GwPlus2ToGwPlus4SignificandMask);
}

return new DecimalIeee754<TSignificand>(signed, exponent - TDecimal.Bias, significand);
}

internal static int CompareDecimalIeee754<TDecimal, TSignificand, TValue>(TValue currentValue, TValue otherValue)
where TDecimal : unmanaged, IDecimalIeee754UnpackInfo<TDecimal, TSignificand, TValue>
where TSignificand : IBinaryInteger<TSignificand>
where TValue : IBinaryInteger<TValue>
{
if (currentValue == otherValue)
{
return 0;
}
DecimalIeee754<TSignificand> current = UnpackDecimalIeee754<TDecimal, TSignificand, TValue>(currentValue);
DecimalIeee754<TSignificand> other = UnpackDecimalIeee754<TDecimal, TSignificand, TValue>(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<TSignificand> biggerExp, DecimalIeee754<TSignificand> 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;
}
}
}
}
Loading