-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
[API Proposal]: Add Decimal32, Decimal64, and Decimal128 from the IEEE 754-2019 standard. #81376
Comments
Tagging subscribers to this area: @dotnet/area-system-numerics Issue DetailsBackground and motivationThis is a restructuring of the original API proposal here: #69777 The existing Alternative Designs
Risks
API Proposal
namespace System.Numerics
{
/// <summary>Defines an IEEE 754 floating-point type that is represented in a base-10 format.</summary>
/// <typeparam name="TSelf">The type that implements the interface.</typeparam>
public interface IDecimalFloatingPointIeee754<TSelf> // PLATINUM
: IFloatingPointIeee754<TSelf>
where TSelf : IDecimalFloatingPointIeee754<TSelf>
{
// IEEE Spec 5.3.2
static abstract TSelf Quantize(TSelf x, TSelf y);
static abstract TSelf Quantum(TSelf x);
// IEEE Spec 5.7.3
static abstract bool SameQuantum(TSelf x, TSelf y);
}
//
// Summary:
// Represents a 32-bit IEEE decimal floating-point number
[StructLayout(LayoutKind.Sequential)]
public readonly struct Decimal32
: IComparable<Decimal32>,
IComparable,
ISpanFormattable,
ISpanParsable<Decimal32>,
IEquatable<Decimal32>,
IFloatingPoint<Decimal32>,/*PLATINUM: Replace with IDecimalFloatingPointIeee754<Decimal32>,*/
IMinMaxValue<Decimal32>
{
internal readonly uint _value;
//
// Parsing (INumberBase, IParsable, ISpanParsable)
//
public static Decimal32 Parse(string s);
public static Decimal32 Parse(string s, NumberStyles style);
public static Decimal32 Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
public static Decimal32 Parse(string s, IFormatProvider? provider);
public static Decimal32 Parse(ReadOnlySpan<char> s, NumberStyles style = DefaultParseStyle, IFormatProvider? provider = null);
public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? provider);
public static bool TryParse([NotNullWhen(true)] string? s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
//
// Misc. Methods (including IComparable, IEquatable)
//
public int CompareTo(object? obj);
public int CompareTo(Decimal32 other);
public override bool Equals([NotNullWhen(true)] object? obj);
public bool Equals(Decimal32 other);
public override int GetHashCode();
// 5.5.2 of the IEEE Spec
public static uint EncodeDecimal(Decimal32 x);
public static Decimal32 DecodeDecimal(uint x);
public static uint EncodeBinary(Decimal32 x);
public static Decimal32 DecodeBinary(uint x);
//
// Formatting (IFormattable, ISpanFormattable)
//
public override string ToString();
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format);
public string ToString(IFormatProvider? provider);
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider);
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
//
// Explicit Convert To Decimal32
// (T -> Decimal32 is lossy)
//
public static explicit operator Decimal32(int value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(uint value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nuint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(long value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(ulong value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(Int128 value);
public static explicit operator Decimal32(UInt128 value);
public static explicit operator Decimal32(Half value);
public static explicit operator Decimal32(float value);
public static explicit operator Decimal32(double value);
public static explicit operator Decimal32(decimal value);
public static explicit operator Decimal32(Decimal64 value);
public static explicit operator Decimal32(Decimal128 value);
//
// Explicit Convert From Decimal32
// (Decimal32 -> T is lossy)
// - Includes a "checked" conversion if T cannot represent infinity and NaN
//
public static explicit operator byte(Decimal32 value);
public static explicit operator checked byte(Decimal32 value);
public static explicit operator sbyte(Decimal32 value);
public static explicit operator checked sbyte(Decimal32 value);
public static explicit operator char(Decimal32 value);
public static explicit operator checked char(Decimal32 value);
public static explicit operator short(Decimal32 value);
public static explicit operator checked short(Decimal32 value);
public static explicit operator ushort(Decimal32 value);
public static explicit operator checked ushort(Decimal32 value);
public static explicit operator int(Decimal32 value);
public static explicit operator checked int(Decimal32 value);
public static explicit operator uint(Decimal32 value);
public static explicit operator checked uint(Decimal32 value);
public static explicit operator nint(Decimal32 value);
public static explicit operator checked nint(Decimal32 value);
public static explicit operator nuint(Decimal32 value);
public static explicit operator checked nuint(Decimal32 value);
public static explicit operator long(Decimal32 value);
public static explicit operator checked long(Decimal32 value);
public static explicit operator ulong(Decimal32 value);
public static explicit operator checked ulong(Decimal32 value);
public static explicit operator Int128(Decimal32 value);
public static explicit operator checked Int128(Decimal32 value);
public static explicit operator UInt128(Decimal32 value);
public static explicit operator checked UInt128(Decimal32 value);
public static explicit operator Half(Decimal32 value);
public static explicit operator float(Decimal32 value);
public static explicit operator double(Decimal32 value);
public static explicit operator decimal(Decimal32 value); // Doesn't have a "checked" for historical reasons
//
// Implicit Convert To Decimal32
// (T -> Decimal32 is not lossy)
//
public static implicit operator Decimal32(byte value);
public static implicit operator Decimal32(sbyte value);
public static implicit operator Decimal32(char value);
public static implicit operator Decimal32(short value);
public static implicit operator Decimal32(ushort value);
//
// Implicit Convert From Decimal32
// (Decimal32 -> T is not lossy)
//
public static implicit operator Decimal64(Decimal32 value);
public static implicit operator Decimal128(Decimal32 value);
//
// IAdditionOperators
//
public static Decimal32 operator +(Decimal32 left, Decimal32 right);
//
// IAdditiveIdentity
//
static Decimal32 IAdditiveIdentity<Decimal32, Decimal32>.AdditiveIdentity;
//
// IComparisonOperators
//
public static bool operator <(Decimal32 left, Decimal32 right);
public static bool operator >(Decimal32 left, Decimal32 right);
public static bool operator <=(Decimal32 left, Decimal32 right);
public static bool operator >=(Decimal32 left, Decimal32 right);
//
// IDecimalFloatingPointIeee754
//
public static Decimal32 Quantize(Decimal32 x, Decimal32 y);
public static Decimal32 Quantum(Decimal32 x);
public static bool SameQuantum(Decimal32 x, Decimal32 y);
//
// IDecrementOperators
//
public static Decimal32 operator --(Decimal32 value);
//
// IDivisionOperators
//
public static Decimal32 operator /(Decimal32 left, Decimal32 right);
//
// IEqualityOperators
//
public static bool operator ==(Decimal32 left, Decimal32 right);
public static bool operator !=(Decimal32 left, Decimal32 right);
//
// IExponentialFunctions
//
public static Decimal32 Exp(Decimal32 x); // PLATINUM
public static Decimal32 ExpM1(Decimal32 x); // PLATINUM
public static Decimal32 Exp2(Decimal32 x); // PLATINUM
public static Decimal32 Exp2M1(Decimal32 x); // PLATINUM
public static Decimal32 Exp10(Decimal32 x); // PLATINUM
public static Decimal32 Exp10M1(Decimal32 x); // PLATINUM
//
// IFloatingPoint
//
public static Decimal32 Ceiling(Decimal32 x);
public static Decimal32 Floor(Decimal32 x);
public static Decimal32 Round(Decimal32 x);
public static Decimal32 Round(Decimal32 x, int digits);
public static Decimal32 Round(Decimal32 x, MidpointRounding mode);
public static Decimal32 Round(Decimal32 x, int digits, MidpointRounding mode);
public static Decimal32 Truncate(Decimal32 x);
int IFloatingPoint<Decimal32>.GetExponentByteCount();
int IFloatingPoint<Decimal32>.GetExponentShortestBitLength();
int IFloatingPoint<Decimal32>.GetSignificandBitLength();
int IFloatingPoint<Decimal32>.GetSignificandByteCount();
bool IFloatingPoint<Decimal32>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten);
//
// IFloatingPointConstants
//
public static Decimal32 E;
public static Decimal32 Pi;
public static Decimal32 Tau;
//
// IFloatingPointIeee754
//
public static Decimal32 Epsilon;
public static Decimal32 NaN;
public static Decimal32 NegativeInfinity;
public static Decimal32 NegativeZero;
public static Decimal32 PositiveInfinity;
public static Decimal32 Atan2(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 Atan2Pi(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 BitDecrement(Decimal32 x);
public static Decimal32 BitIncrement(Decimal32 x);
public static Decimal32 FusedMultiplyAdd(Decimal32 left, Decimal32 right, Decimal32 addend);
public static Decimal32 Ieee754Remainder(Decimal32 left, Decimal32 right);
public static int ILogB(Decimal32 x);
public static Decimal32 Lerp(Decimal32 value1, Decimal32 value2, Decimal32 amount);
public static Decimal32 ReciprocalEstimate(Decimal32 x);
public static Decimal32 ReciprocalSqrtEstimate(Decimal32 x);
public static Decimal32 ScaleB(Decimal32 x, int n);
// public static Decimal32 Compound(Half x, Decimal32 n); (Already approved in API review but not implemented yet) // PLATINUM
//
// IHyperbolicFunctions
//
public static Decimal32 Acosh(Decimal32 x); // PLATINUM
public static Decimal32 Asinh(Decimal32 x); // PLATINUM
public static Decimal32 Atanh(Decimal32 x); // PLATINUM
public static Decimal32 Cosh(Decimal32 x); // PLATINUM
public static Decimal32 Sinh(Decimal32 x); // PLATINUM
public static Decimal32 Tanh(Decimal32 x); // PLATINUM
//
// IIncrementOperators
//
public static Decimal32 operator ++(Decimal32 value);
//
// ILogarithmicFunctions
//
public static Decimal32 Log(Decimal32 x); // PLATINUM
public static Decimal32 Log(Decimal32 x, Decimal32 newBase); // PLATINUM
public static Decimal32 Log10(Decimal32 x); // PLATINUM
public static Decimal32 LogP1(Decimal32 x); // PLATINUM
public static Decimal32 Log2(Decimal32 x); // PLATINUM
public static Decimal32 Log2P1(Decimal32 x); // PLATINUM
public static Decimal32 Log10P1(Decimal32 x); // PLATINUM
//
// IMinMaxValue
//
public static Decimal32 MaxValue;
public static Decimal32 MinValue;
//
// IModulusOperators
//
public static Decimal32 operator %(Decimal32 left, Decimal32 right);
//
// IMultiplicativeIdentity
//
public static Decimal32 MultiplicativeIdentity;
//
// IMultiplyOperators
//
public static Decimal32 operator *(Decimal32 left, Decimal32 right);
//
// INumber
//
public static Decimal32 Clamp(Decimal32 value, Decimal32 min, Decimal32 max);
public static Decimal32 CopySign(Decimal32 value, Decimal32 sign);
public static Decimal32 Max(Decimal32 x, Decimal32 y);
public static Decimal32 MaxNumber(Decimal32 x, Decimal32 y);
public static Decimal32 Min(Decimal32 x, Decimal32 y);
public static Decimal32 MinNumber(Decimal32 x, Decimal32 y);
public static int Sign(Decimal32 value);
//
// INumberBase (well defined/commonly used values)
//
public static Decimal32 One;
static int INumberBase<Decimal32>.Radix; // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 Zero;
public static Decimal32 Abs(Decimal32 value);
public static Decimal32 CreateChecked<TOther>(TOther value);
public static Decimal32 CreateSaturating<TOther>(TOther value);
public static Decimal32 CreateTruncating<TOther>(TOther value);
static bool INumberBase<Decimal32>.IsCanonical(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
static bool INumberBase<Decimal32>.IsComplexNumber(Decimal32 value);
public static bool IsEvenInteger(Decimal32 value);
public static bool IsFinite(Decimal32 value);
static bool INumberBase<Decimal32>.IsImaginaryNumber(Decimal32 value);
public static bool IsInfinity(Decimal32 value);
public static bool IsInteger(Decimal32 value);
public static bool IsNaN(Decimal32 value);
public static bool IsNegative(Decimal32 value);
public static bool IsNegativeInfinity(Decimal32 value);
public static bool IsNormal(Decimal32 value);
public static bool IsOddInteger(Decimal32 value);
public static bool IsPositive(Decimal32 value);
public static bool IsPositiveInfinity(Decimal32 value);
public static bool IsRealNumber(Decimal32 value);
public static bool IsSubnormal(Decimal32 value);
static bool INumberBase<Decimal32>.IsZero(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 MaxMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MaxMagnitudeNumber(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitudeNumber(Decimal32 x, Decimal32 y);
static bool INumberBase<Decimal32>.TryConvertFromChecked<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromSaturating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromTruncating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertToChecked<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToSaturating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToTruncating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
//
// IPowerFunctions
//
public static Decimal32 Pow(Decimal32 x, Decimal32 y); // PLATINUM
//
// IRootFunctions
//
public static Decimal32 Cbrt(Decimal32 x); // PLATINUM
public static Decimal32 Hypot(Decimal32 x, Decimal32 y); // PLATINUM
public static Decimal32 RootN(Decimal32 x, int n); // PLATINUM
public static Decimal32 Sqrt(Decimal32 x);
//
// ISignedNumber
//
public static Decimal32 NegativeOne;
//
// ISubtractionOperators
//
public static Decimal32 operator -(Decimal32 left, Decimal32 right);
//
// ITrigonometricFunctions
//
public static Decimal32 Acos(Decimal32 x); // PLATINUM
public static Decimal32 AcosPi(Decimal32 x); // PLATINUM
public static Decimal32 Asin(Decimal32 x); // PLATINUM
public static Decimal32 AsinPi(Decimal32 x); // PLATINUM
public static Decimal32 Atan(Decimal32 x); // PLATINUM
public static Decimal32 AtanPi(Decimal32 x); // PLATINUM
public static Decimal32 Cos(Decimal32 x); // PLATINUM
public static Decimal32 CosPi(Decimal32 x); // PLATINUM
public static Decimal32 Sin(Decimal32 x); // PLATINUM
public static (Decimal32 Sin, Decimal32 Cos) SinCos(Decimal32 x); // PLATINUM
public static (Decimal32 SinPi, Decimal32 CosPi) SinCosPi(Decimal32 x); // PLATINUM
public static Decimal32 SinPi(Decimal32 x); // PLATINUM
public static Decimal32 Tan(Decimal32 x); // PLATINUM
public static Decimal32 TanPi(Decimal32 x); // PLATINUM
//
// IUnaryNegationOperators
//
public static Decimal32 operator -(Decimal32 value);
//
// IUnaryPlusOperators
//
public static Decimal32 operator +(Decimal32 value);
}
}
|
This is a bit strong. In order for customers to benefit from future hardware these APIs must be implemented. The above implies these APIs should be implemented because of future hardware which doesn't seem correct. This is an increasingly niche API surface area and I think we should determine a customer and if it is a current need for .NET 8 or perhaps a future release. |
@AaronRobinsonMSFT Let me adjust my wording, what I meant to imply with that bullet point was that if we are going to implement these types, we must implement them with future hardware support in mind. In other words, we should implement them in such a way that, if hardware support is eventually released and widespread, we can easily wire these up to those intrinsics. |
@AaronRobinsonMSFT, I think you're misunderstanding the statement here. The statement is one around ensuring we consider that there are two backing encodings for decimal (one oriented towards software and another towards hardware) and that we should ensure our API surface isn't forcing one or the other. Such hardware supporting the IEEE 754 decimal types already exists and has been in production use at the enterprise level for years (namely IBM PowerPC).
I likewise think this is a misunderstanding. We have had The IEEE 754 decimal types are an industry standard that has already been proven to be successful, which has been an ABI standard for over 12 years, and which is actively getting first class support in a number of other languages. This points towards it being a viable answer towards both the perf and extension points customers have already been asking for around |
Gotcha. That makes sense. @dakersnar's statement also helped to clarify that. |
Copying my comment from #79004 and adding a little more context. I'm the current lead developer of Otterkit, a free and open source COBOL compiler for dotnet (Implementing the COBOL 2022 standard). We're looking forward to being able to use these types in our compiler. We are currently PInvoking calls to the mpdecimal library to provide this functionality on dotnet, but that makes the build process much more complicated and hurts portability. Having these types available directly on C# would be awesome for our project. The reason for this is that COBOL relies heavily on decimal arithmetic, the COBOL standard requires support for the Decimal128 type for its standard-decimal arithmetic mode, and requires a decimal implementation with at least 31 digits of precision for its native arithmetic mode. The decimal type we have in C# right now is not compatible with these requirements. Our COBOL runtime library only requires the Decimal128 type, both the Decimal32 and Decimal64 use a truncated Decimal128. This works quite well because all of these types' max/min values only contains 9s and an exponent ( Hi @dakersnar, COBOL provides some of those math functions with its intrinsic functions, and implementing those is required by the COBOL standard. The mpdecimal library that we're using unfortunately does not completely provide that functionality so we had to implement it ourselves in the runtime library (mpdecimal doesn't provide trigonometric functions). Because of the COBOL standard's requirements we would require support for the COBOL itself does not provide support for anything from Having those directly in C# would be awesome, we would be able to provide that COBOL functionality in our compiler in a way that is compatible with C#, and might make it easier to implement our COBOL <=> C# Bridge in the future. I'm not sure if I'm allowed to request any extra functionality for this, but if possible, would there be a way to support UTF-8 encoded |
Please consider yourself "allowed". I'm sure the team would appreciate knowing more about your needs, particularly to the end of influencing their design and prioritization. |
@KTSnowy just so I can fully understand your scenario, let me outline a few possibilities:
At which level does it become feasible for you to plug our types into your project, if only to test the ease of integration and measure performance? Following that, at which level does it become beneficial to plug the types into your project? Obviously, it sounds like level 3 is what you would most prefer. In the event level 3 is not feasible for this release cycle, is there a "half measure" for .NET 8 that you would still be able to use? Edit: Additionally, I want to note that .NET does not currently support some aspects of the IEEE spec including global rounding modes, floating-point exceptions, and flag setting. Does your project require these features? |
Hi @dakersnar, it would become both feasible and beneficial to plug these types into our compiler at level 2, but only if there is support for UTF-8 encoded Spans (from Span and to Span conversions). The COBOL standard requires support for the Decimal128 type. At level 1 we won't be able to use them due to the Decimal64 format only supporting 16 decimal digits. At level 2 with UTF-8 Spans support we could move most of our mpdecimal calls to the .NET 8 implementation, we could also temporarily use the Platinum APIs functionality from mpdecimal until there is support in the .NET runtime for them. This would mostly depend on the UTF-8 Without it, PInvoking into mpdecimal to provide the Platinum APIs functionality would become a lot more expensive. Having to convert from and to a string and passing them through PInvoke would be much more expensive than the byte pointers that we're currently using. Without conversions for UTF-8 encoded Spans, it would only be feasible at level 3.
Our compiler can generate calls to the local rounding methods to "emulate" the global rounding modes at compile time, so this won't be an issue, but it would be awesome to have those if it's possible to support them. The exceptions and flags are also not required, COBOL has a very different way of handling exceptions so we won't be able to directly use C# exceptions. |
I'd be happy to help with performance and ease of integration tests. Just let me know if there's any specific way or specific tool that you'd like us to use to measure performance. |
Would the "EncodeBinary" and/or "EncodeDecimal" APIs proposed above work for your scenario? That would get you the raw bit representation of the Decimal128, either as a UInt128 or two ulongs, depending on what API review decides. |
It wouldn't work completely, we still need a "byte array-like" representation of the Decimal128 to apply formatting to. Using Ideally we'd like a "ToSpan", "Parse" and "TryParse" that can return and accept UTF-8 encoded |
@dakersnar Looks like they want |
I think we should consider
|
Another consideration: reading another recent thread, I was reminded that we expose a public constructor for Notably, with the current shape, users can only create these types in the following ways:
|
Since we lost quorum before finalizing the ctor-vs-named-static point (and the names of the parameters), and the pattern for the conversion operators, we'll need to discuss this again before approval. namespace System.Numerics
{
/// <summary>Defines an IEEE 754 floating-point type that is represented in a base-10 format.</summary>
/// <typeparam name="TSelf">The type that implements the interface.</typeparam>
public interface IDecimalFloatingPointIeee754<TSelf> // PLATINUM
: IFloatingPointIeee754<TSelf>
where TSelf : IDecimalFloatingPointIeee754<TSelf>
{
// IEEE Spec 5.3.2
static abstract TSelf Quantize(TSelf x, TSelf y);
static abstract TSelf GetQuantum(TSelf x);
// IEEE Spec 5.7.3
static abstract bool HaveSameQuantum(TSelf x, TSelf y);
}
//
// Summary:
// Represents a 32-bit IEEE decimal floating-point number
[StructLayout(LayoutKind.Sequential)]
public readonly struct Decimal32
: IComparable<Decimal32>,
IComparable,
ISpanFormattable,
ISpanParsable<Decimal32>,
IEquatable<Decimal32>,
IFloatingPoint<Decimal32>,/*PLATINUM: Replace with IDecimalFloatingPointIeee754<Decimal32>,*/
IMinMaxValue<Decimal32>
{
internal readonly uint _value; // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
//
// Parsing (INumberBase, IParsable, ISpanParsable, other)
//
public static Decimal32 Parse(string s);
public static Decimal32 Parse(string s, NumberStyles style);
public static Decimal32 Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
public static Decimal32 Parse(string s, IFormatProvider? provider);
public static Decimal32 Parse(ReadOnlySpan<char> s, NumberStyles style = DefaultParseStyle, IFormatProvider? provider = null);
public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? provider);
public static bool TryParse([NotNullWhen(true)] string? s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
//
// Misc. Methods (including IComparable, IEquatable, other)
//
public int CompareTo(object? obj);
public int CompareTo(Decimal32 other);
public override bool Equals([NotNullWhen(true)] object? obj);
public bool Equals(Decimal32 other);
public override int GetHashCode();
// 5.5.2 of the IEEE Spec
public static uint EncodeDecimal(Decimal32 x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static Decimal32 DecodeDecimal(uint x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static uint EncodeBinary(Decimal32 x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static Decimal32 DecodeBinary(uint x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
//
// Formatting (IFormattable, ISpanFormattable, other)
//
public override string ToString();
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format);
public string ToString(IFormatProvider? provider);
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider);
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
//
// Explicit Convert To Decimal32
// (T -> Decimal32 is lossy)
//
public static explicit operator Decimal32(int value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(uint value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nuint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(long value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(ulong value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(Int128 value);
public static explicit operator Decimal32(UInt128 value);
public static explicit operator Decimal32(Half value);
public static explicit operator Decimal32(float value);
public static explicit operator Decimal32(double value);
public static explicit operator Decimal32(decimal value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(Decimal64 value);
public static explicit operator Decimal32(Decimal128 value);
//
// Explicit Convert From Decimal32
// (Decimal32 -> T is lossy)
// - Includes a "checked" conversion if T cannot represent infinity and NaN
//
public static explicit operator byte(Decimal32 value);
public static explicit operator checked byte(Decimal32 value);
public static explicit operator sbyte(Decimal32 value);
public static explicit operator checked sbyte(Decimal32 value);
public static explicit operator char(Decimal32 value);
public static explicit operator checked char(Decimal32 value);
public static explicit operator short(Decimal32 value);
public static explicit operator checked short(Decimal32 value);
public static explicit operator ushort(Decimal32 value);
public static explicit operator checked ushort(Decimal32 value);
public static explicit operator int(Decimal32 value);
public static explicit operator checked int(Decimal32 value);
public static explicit operator uint(Decimal32 value);
public static explicit operator checked uint(Decimal32 value);
public static explicit operator nint(Decimal32 value);
public static explicit operator checked nint(Decimal32 value);
public static explicit operator nuint(Decimal32 value);
public static explicit operator checked nuint(Decimal32 value);
public static explicit operator long(Decimal32 value);
public static explicit operator checked long(Decimal32 value);
public static explicit operator ulong(Decimal32 value);
public static explicit operator checked ulong(Decimal32 value);
public static explicit operator Int128(Decimal32 value);
public static explicit operator checked Int128(Decimal32 value);
public static explicit operator UInt128(Decimal32 value);
public static explicit operator checked UInt128(Decimal32 value);
public static explicit operator Half(Decimal32 value);
public static explicit operator float(Decimal32 value);
public static explicit operator double(Decimal32 value);
public static explicit operator decimal(Decimal32 value); // Doesn't have a "checked" for historical reasons
//
// Implicit Convert To Decimal32
// (T -> Decimal32 is not lossy)
//
public static implicit operator Decimal32(byte value);
public static implicit operator Decimal32(sbyte value);
public static implicit operator Decimal32(char value);
public static implicit operator Decimal32(short value);
public static implicit operator Decimal32(ushort value);
//
// Implicit Convert From Decimal32
// (Decimal32 -> T is not lossy)
//
public static implicit operator Decimal64(Decimal32 value);
public static implicit operator Decimal128(Decimal32 value);
//
// IAdditionOperators
//
public static Decimal32 operator +(Decimal32 left, Decimal32 right);
//
// IAdditiveIdentity
//
static Decimal32 IAdditiveIdentity<Decimal32, Decimal32>.AdditiveIdentity { get; }
//
// IComparisonOperators
//
public static bool operator <(Decimal32 left, Decimal32 right);
public static bool operator >(Decimal32 left, Decimal32 right);
public static bool operator <=(Decimal32 left, Decimal32 right);
public static bool operator >=(Decimal32 left, Decimal32 right);
//
// IDecimalFloatingPointIeee754
//
public static Decimal32 Quantize(Decimal32 x, Decimal32 y);
public static Decimal32 Quantum(Decimal32 x);
public static bool SameQuantum(Decimal32 x, Decimal32 y);
//
// IDecrementOperators
//
public static Decimal32 operator --(Decimal32 value);
//
// IDivisionOperators
//
public static Decimal32 operator /(Decimal32 left, Decimal32 right);
//
// IEqualityOperators
//
public static bool operator ==(Decimal32 left, Decimal32 right);
public static bool operator !=(Decimal32 left, Decimal32 right);
//
// IExponentialFunctions
//
public static Decimal32 Exp(Decimal32 x); // PLATINUM
public static Decimal32 ExpM1(Decimal32 x); // PLATINUM
public static Decimal32 Exp2(Decimal32 x); // PLATINUM
public static Decimal32 Exp2M1(Decimal32 x); // PLATINUM
public static Decimal32 Exp10(Decimal32 x); // PLATINUM
public static Decimal32 Exp10M1(Decimal32 x); // PLATINUM
//
// IFloatingPoint
//
public static Decimal32 Ceiling(Decimal32 x);
public static Decimal32 Floor(Decimal32 x);
public static Decimal32 Round(Decimal32 x);
public static Decimal32 Round(Decimal32 x, int digits);
public static Decimal32 Round(Decimal32 x, MidpointRounding mode);
public static Decimal32 Round(Decimal32 x, int digits, MidpointRounding mode);
public static Decimal32 Truncate(Decimal32 x);
int IFloatingPoint<Decimal32>.GetExponentByteCount();
int IFloatingPoint<Decimal32>.GetExponentShortestBitLength();
int IFloatingPoint<Decimal32>.GetSignificandBitLength();
int IFloatingPoint<Decimal32>.GetSignificandByteCount();
bool IFloatingPoint<Decimal32>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten);
//
// IFloatingPointConstants
//
public static Decimal32 E { get; }
public static Decimal32 Pi { get; }
public static Decimal32 Tau { get; }
//
// IFloatingPointIeee754
//
public static Decimal32 Epsilon { get; }
public static Decimal32 NaN { get; }
public static Decimal32 NegativeInfinity { get; }
public static Decimal32 NegativeZero { get; }
public static Decimal32 PositiveInfinity { get; }
public static Decimal32 Atan2(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 Atan2Pi(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 BitDecrement(Decimal32 x);
public static Decimal32 BitIncrement(Decimal32 x);
public static Decimal32 FusedMultiplyAdd(Decimal32 left, Decimal32 right, Decimal32 addend);
public static Decimal32 Ieee754Remainder(Decimal32 left, Decimal32 right);
public static int ILogB(Decimal32 x);
public static Decimal32 Lerp(Decimal32 value1, Decimal32 value2, Decimal32 amount);
public static Decimal32 ReciprocalEstimate(Decimal32 x);
public static Decimal32 ReciprocalSqrtEstimate(Decimal32 x);
public static Decimal32 ScaleB(Decimal32 x, int n);
// public static Decimal32 Compound(Half x, Decimal32 n); (Already approved in API review but not implemented yet) // PLATINUM
//
// IHyperbolicFunctions
//
public static Decimal32 Acosh(Decimal32 x); // PLATINUM
public static Decimal32 Asinh(Decimal32 x); // PLATINUM
public static Decimal32 Atanh(Decimal32 x); // PLATINUM
public static Decimal32 Cosh(Decimal32 x); // PLATINUM
public static Decimal32 Sinh(Decimal32 x); // PLATINUM
public static Decimal32 Tanh(Decimal32 x); // PLATINUM
//
// IIncrementOperators
//
public static Decimal32 operator ++(Decimal32 value);
//
// ILogarithmicFunctions
//
public static Decimal32 Log(Decimal32 x); // PLATINUM
public static Decimal32 Log(Decimal32 x, Decimal32 newBase); // PLATINUM
public static Decimal32 Log10(Decimal32 x); // PLATINUM
public static Decimal32 LogP1(Decimal32 x); // PLATINUM
public static Decimal32 Log2(Decimal32 x); // PLATINUM
public static Decimal32 Log2P1(Decimal32 x); // PLATINUM
public static Decimal32 Log10P1(Decimal32 x); // PLATINUM
//
// IMinMaxValue
//
public static Decimal32 MaxValue { get; }
public static Decimal32 MinValue { get; }
//
// IModulusOperators
//
public static Decimal32 operator %(Decimal32 left, Decimal32 right);
//
// IMultiplicativeIdentity
//
public static Decimal32 MultiplicativeIdentity { get; }
//
// IMultiplyOperators
//
public static Decimal32 operator *(Decimal32 left, Decimal32 right);
//
// INumber
//
public static Decimal32 Clamp(Decimal32 value, Decimal32 min, Decimal32 max);
public static Decimal32 CopySign(Decimal32 value, Decimal32 sign);
public static Decimal32 Max(Decimal32 x, Decimal32 y);
public static Decimal32 MaxNumber(Decimal32 x, Decimal32 y);
public static Decimal32 Min(Decimal32 x, Decimal32 y);
public static Decimal32 MinNumber(Decimal32 x, Decimal32 y);
public static int Sign(Decimal32 value);
//
// INumberBase (well defined/commonly used values)
//
public static Decimal32 One { get; }
static int INumberBase<Decimal32>.Radix; // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 Zero { get; }
public static Decimal32 Abs(Decimal32 value);
public static Decimal32 CreateChecked<TOther>(TOther value);
public static Decimal32 CreateSaturating<TOther>(TOther value);
public static Decimal32 CreateTruncating<TOther>(TOther value);
static bool INumberBase<Decimal32>.IsCanonical(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
static bool INumberBase<Decimal32>.IsComplexNumber(Decimal32 value);
public static bool IsEvenInteger(Decimal32 value);
public static bool IsFinite(Decimal32 value);
static bool INumberBase<Decimal32>.IsImaginaryNumber(Decimal32 value);
public static bool IsInfinity(Decimal32 value);
public static bool IsInteger(Decimal32 value);
public static bool IsNaN(Decimal32 value);
public static bool IsNegative(Decimal32 value);
public static bool IsNegativeInfinity(Decimal32 value);
public static bool IsNormal(Decimal32 value);
public static bool IsOddInteger(Decimal32 value);
public static bool IsPositive(Decimal32 value);
public static bool IsPositiveInfinity(Decimal32 value);
public static bool IsRealNumber(Decimal32 value);
public static bool IsSubnormal(Decimal32 value);
static bool INumberBase<Decimal32>.IsZero(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 MaxMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MaxMagnitudeNumber(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitudeNumber(Decimal32 x, Decimal32 y);
static bool INumberBase<Decimal32>.TryConvertFromChecked<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromSaturating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromTruncating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertToChecked<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToSaturating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToTruncating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
//
// IPowerFunctions
//
public static Decimal32 Pow(Decimal32 x, Decimal32 y); // PLATINUM
//
// IRootFunctions
//
public static Decimal32 Cbrt(Decimal32 x); // PLATINUM
public static Decimal32 Hypot(Decimal32 x, Decimal32 y); // PLATINUM
public static Decimal32 RootN(Decimal32 x, int n); // PLATINUM
public static Decimal32 Sqrt(Decimal32 x);
//
// ISignedNumber
//
public static Decimal32 NegativeOne { get; }
//
// ISubtractionOperators
//
public static Decimal32 operator -(Decimal32 left, Decimal32 right);
//
// ITrigonometricFunctions
//
public static Decimal32 Acos(Decimal32 x); // PLATINUM
public static Decimal32 AcosPi(Decimal32 x); // PLATINUM
public static Decimal32 Asin(Decimal32 x); // PLATINUM
public static Decimal32 AsinPi(Decimal32 x); // PLATINUM
public static Decimal32 Atan(Decimal32 x); // PLATINUM
public static Decimal32 AtanPi(Decimal32 x); // PLATINUM
public static Decimal32 Cos(Decimal32 x); // PLATINUM
public static Decimal32 CosPi(Decimal32 x); // PLATINUM
public static Decimal32 Sin(Decimal32 x); // PLATINUM
public static (Decimal32 Sin, Decimal32 Cos) SinCos(Decimal32 x); // PLATINUM
public static (Decimal32 SinPi, Decimal32 CosPi) SinCosPi(Decimal32 x); // PLATINUM
public static Decimal32 SinPi(Decimal32 x); // PLATINUM
public static Decimal32 Tan(Decimal32 x); // PLATINUM
public static Decimal32 TanPi(Decimal32 x); // PLATINUM
//
// IUnaryNegationOperators
//
public static Decimal32 operator -(Decimal32 value);
//
// IUnaryPlusOperators
//
public static Decimal32 operator +(Decimal32 value);
}
} |
Since we lost quorum before finalizing the ctor-vs-named-static point (and the names of the parameters), and the pattern for the conversion operators, we'll need to discuss this again before approval. namespace System.Numerics
{
/// <summary>Defines an IEEE 754 floating-point type that is represented in a base-10 format.</summary>
/// <typeparam name="TSelf">The type that implements the interface.</typeparam>
public interface IDecimalFloatingPointIeee754<TSelf> // PLATINUM
: IFloatingPointIeee754<TSelf>
where TSelf : IDecimalFloatingPointIeee754<TSelf>
{
// IEEE Spec 5.3.2
static abstract TSelf Quantize(TSelf x, TSelf y);
static abstract TSelf GetQuantum(TSelf x);
// IEEE Spec 5.7.3
static abstract bool HaveSameQuantum(TSelf x, TSelf y);
}
//
// Summary:
// Represents a 32-bit IEEE decimal floating-point number
[StructLayout(LayoutKind.Sequential)]
public readonly struct Decimal32
: IComparable<Decimal32>,
IComparable,
ISpanFormattable,
ISpanParsable<Decimal32>,
IEquatable<Decimal32>,
IFloatingPoint<Decimal32>,/*PLATINUM: Replace with IDecimalFloatingPointIeee754<Decimal32>,*/
IMinMaxValue<Decimal32>
{
internal readonly uint _value; // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public Decimal32(int significand, int exponent); // NOTE: params are (long, int) for Decimal64 and (Int128, int) for Decimal128
//
// Parsing (INumberBase, IParsable, ISpanParsable, other)
//
public static Decimal32 Parse(string s);
public static Decimal32 Parse(string s, NumberStyles style);
public static Decimal32 Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
public static Decimal32 Parse(string s, IFormatProvider? provider);
public static Decimal32 Parse(ReadOnlySpan<char> s, NumberStyles style = DefaultParseStyle, IFormatProvider? provider = null);
public static Decimal32 Parse(string s, NumberStyles style, IFormatProvider? provider);
public static bool TryParse([NotNullWhen(true)] string? s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Decimal32 result);
//
// Misc. Methods (including IComparable, IEquatable, other)
//
public int CompareTo(object? obj);
public int CompareTo(Decimal32 other);
public override bool Equals([NotNullWhen(true)] object? obj);
public bool Equals(Decimal32 other);
public override int GetHashCode();
// 5.5.2 of the IEEE Spec
public static uint EncodeDecimal(Decimal32 x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static Decimal32 DecodeDecimal(uint x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static uint EncodeBinary(Decimal32 x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
public static Decimal32 DecodeBinary(uint x); // NOTE: this is a ulong for Decimal64, and a UInt128 for Decimal128
//
// Formatting (IFormattable, ISpanFormattable, other)
//
public override string ToString();
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format);
public string ToString(IFormatProvider? provider);
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider);
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null);
//
// Explicit Convert To Decimal32
// (T -> Decimal32 is lossy)
//
public static explicit operator Decimal32(int value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(uint value); // NOTE: Decimal64 and Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(nuint value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(long value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(ulong value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(Int128 value);
public static explicit operator Decimal32(UInt128 value);
public static explicit operator Decimal32(Half value);
public static explicit operator Decimal32(float value);
public static explicit operator Decimal32(double value);
public static explicit operator Decimal32(decimal value); // NOTE: Decimal128 will have this as *implicit*
public static explicit operator Decimal32(Decimal64 value);
public static explicit operator Decimal32(Decimal128 value);
//
// Explicit Convert From Decimal32
// (Decimal32 -> T is lossy)
// - Includes a "checked" conversion if T cannot represent infinity and NaN
//
public static explicit operator byte(Decimal32 value);
public static explicit operator checked byte(Decimal32 value);
public static explicit operator sbyte(Decimal32 value);
public static explicit operator checked sbyte(Decimal32 value);
public static explicit operator char(Decimal32 value);
public static explicit operator checked char(Decimal32 value);
public static explicit operator short(Decimal32 value);
public static explicit operator checked short(Decimal32 value);
public static explicit operator ushort(Decimal32 value);
public static explicit operator checked ushort(Decimal32 value);
public static explicit operator int(Decimal32 value);
public static explicit operator checked int(Decimal32 value);
public static explicit operator uint(Decimal32 value);
public static explicit operator checked uint(Decimal32 value);
public static explicit operator nint(Decimal32 value);
public static explicit operator checked nint(Decimal32 value);
public static explicit operator nuint(Decimal32 value);
public static explicit operator checked nuint(Decimal32 value);
public static explicit operator long(Decimal32 value);
public static explicit operator checked long(Decimal32 value);
public static explicit operator ulong(Decimal32 value);
public static explicit operator checked ulong(Decimal32 value);
public static explicit operator Int128(Decimal32 value);
public static explicit operator checked Int128(Decimal32 value);
public static explicit operator UInt128(Decimal32 value);
public static explicit operator checked UInt128(Decimal32 value);
public static explicit operator Half(Decimal32 value);
public static explicit operator float(Decimal32 value);
public static explicit operator double(Decimal32 value);
public static explicit operator decimal(Decimal32 value); // Doesn't have a "checked" for historical reasons
//
// Implicit Convert To Decimal32
// (T -> Decimal32 is not lossy)
//
public static implicit operator Decimal32(byte value);
public static implicit operator Decimal32(sbyte value);
public static implicit operator Decimal32(char value);
public static implicit operator Decimal32(short value);
public static implicit operator Decimal32(ushort value);
//
// Implicit Convert From Decimal32
// (Decimal32 -> T is not lossy)
//
public static implicit operator Decimal64(Decimal32 value);
public static implicit operator Decimal128(Decimal32 value);
//
// IAdditionOperators
//
public static Decimal32 operator +(Decimal32 left, Decimal32 right);
//
// IAdditiveIdentity
//
static Decimal32 IAdditiveIdentity<Decimal32, Decimal32>.AdditiveIdentity { get; }
//
// IComparisonOperators
//
public static bool operator <(Decimal32 left, Decimal32 right);
public static bool operator >(Decimal32 left, Decimal32 right);
public static bool operator <=(Decimal32 left, Decimal32 right);
public static bool operator >=(Decimal32 left, Decimal32 right);
//
// IDecimalFloatingPointIeee754
//
public static Decimal32 Quantize(Decimal32 x, Decimal32 y);
public static Decimal32 Quantum(Decimal32 x);
public static bool SameQuantum(Decimal32 x, Decimal32 y);
//
// IDecrementOperators
//
public static Decimal32 operator --(Decimal32 value);
//
// IDivisionOperators
//
public static Decimal32 operator /(Decimal32 left, Decimal32 right);
//
// IEqualityOperators
//
public static bool operator ==(Decimal32 left, Decimal32 right);
public static bool operator !=(Decimal32 left, Decimal32 right);
//
// IExponentialFunctions
//
public static Decimal32 Exp(Decimal32 x); // PLATINUM
public static Decimal32 ExpM1(Decimal32 x); // PLATINUM
public static Decimal32 Exp2(Decimal32 x); // PLATINUM
public static Decimal32 Exp2M1(Decimal32 x); // PLATINUM
public static Decimal32 Exp10(Decimal32 x); // PLATINUM
public static Decimal32 Exp10M1(Decimal32 x); // PLATINUM
//
// IFloatingPoint
//
public static Decimal32 Ceiling(Decimal32 x);
public static Decimal32 Floor(Decimal32 x);
public static Decimal32 Round(Decimal32 x);
public static Decimal32 Round(Decimal32 x, int digits);
public static Decimal32 Round(Decimal32 x, MidpointRounding mode);
public static Decimal32 Round(Decimal32 x, int digits, MidpointRounding mode);
public static Decimal32 Truncate(Decimal32 x);
int IFloatingPoint<Decimal32>.GetExponentByteCount();
int IFloatingPoint<Decimal32>.GetExponentShortestBitLength();
int IFloatingPoint<Decimal32>.GetSignificandBitLength();
int IFloatingPoint<Decimal32>.GetSignificandByteCount();
bool IFloatingPoint<Decimal32>.TryWriteExponentBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteExponentLittleEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandBigEndian(Span<byte> destination, out int bytesWritten);
bool IFloatingPoint<Decimal32>.TryWriteSignificandLittleEndian(Span<byte> destination, out int bytesWritten);
//
// IFloatingPointConstants
//
public static Decimal32 E { get; }
public static Decimal32 Pi { get; }
public static Decimal32 Tau { get; }
//
// IFloatingPointIeee754
//
public static Decimal32 Epsilon { get; }
public static Decimal32 NaN { get; }
public static Decimal32 NegativeInfinity { get; }
public static Decimal32 NegativeZero { get; }
public static Decimal32 PositiveInfinity { get; }
public static Decimal32 Atan2(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 Atan2Pi(Decimal32 y, Decimal32 x); // PLATINUM
public static Decimal32 BitDecrement(Decimal32 x);
public static Decimal32 BitIncrement(Decimal32 x);
public static Decimal32 FusedMultiplyAdd(Decimal32 left, Decimal32 right, Decimal32 addend);
public static Decimal32 Ieee754Remainder(Decimal32 left, Decimal32 right);
public static int ILogB(Decimal32 x);
public static Decimal32 Lerp(Decimal32 value1, Decimal32 value2, Decimal32 amount);
public static Decimal32 ReciprocalEstimate(Decimal32 x);
public static Decimal32 ReciprocalSqrtEstimate(Decimal32 x);
public static Decimal32 ScaleB(Decimal32 x, int n);
// public static Decimal32 Compound(Half x, Decimal32 n); (Already approved in API review but not implemented yet) // PLATINUM
//
// IHyperbolicFunctions
//
public static Decimal32 Acosh(Decimal32 x); // PLATINUM
public static Decimal32 Asinh(Decimal32 x); // PLATINUM
public static Decimal32 Atanh(Decimal32 x); // PLATINUM
public static Decimal32 Cosh(Decimal32 x); // PLATINUM
public static Decimal32 Sinh(Decimal32 x); // PLATINUM
public static Decimal32 Tanh(Decimal32 x); // PLATINUM
//
// IIncrementOperators
//
public static Decimal32 operator ++(Decimal32 value);
//
// ILogarithmicFunctions
//
public static Decimal32 Log(Decimal32 x); // PLATINUM
public static Decimal32 Log(Decimal32 x, Decimal32 newBase); // PLATINUM
public static Decimal32 Log10(Decimal32 x); // PLATINUM
public static Decimal32 LogP1(Decimal32 x); // PLATINUM
public static Decimal32 Log2(Decimal32 x); // PLATINUM
public static Decimal32 Log2P1(Decimal32 x); // PLATINUM
public static Decimal32 Log10P1(Decimal32 x); // PLATINUM
//
// IMinMaxValue
//
public static Decimal32 MaxValue { get; }
public static Decimal32 MinValue { get; }
//
// IModulusOperators
//
public static Decimal32 operator %(Decimal32 left, Decimal32 right);
//
// IMultiplicativeIdentity
//
public static Decimal32 MultiplicativeIdentity { get; }
//
// IMultiplyOperators
//
public static Decimal32 operator *(Decimal32 left, Decimal32 right);
//
// INumber
//
public static Decimal32 Clamp(Decimal32 value, Decimal32 min, Decimal32 max);
public static Decimal32 CopySign(Decimal32 value, Decimal32 sign);
public static Decimal32 Max(Decimal32 x, Decimal32 y);
public static Decimal32 MaxNumber(Decimal32 x, Decimal32 y);
public static Decimal32 Min(Decimal32 x, Decimal32 y);
public static Decimal32 MinNumber(Decimal32 x, Decimal32 y);
public static int Sign(Decimal32 value);
//
// INumberBase (well defined/commonly used values)
//
public static Decimal32 One { get; }
static int INumberBase<Decimal32>.Radix; // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 Zero { get; }
public static Decimal32 Abs(Decimal32 value);
public static Decimal32 CreateChecked<TOther>(TOther value);
public static Decimal32 CreateSaturating<TOther>(TOther value);
public static Decimal32 CreateTruncating<TOther>(TOther value);
static bool INumberBase<Decimal32>.IsCanonical(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
static bool INumberBase<Decimal32>.IsComplexNumber(Decimal32 value);
public static bool IsEvenInteger(Decimal32 value);
public static bool IsFinite(Decimal32 value);
static bool INumberBase<Decimal32>.IsImaginaryNumber(Decimal32 value);
public static bool IsInfinity(Decimal32 value);
public static bool IsInteger(Decimal32 value);
public static bool IsNaN(Decimal32 value);
public static bool IsNegative(Decimal32 value);
public static bool IsNegativeInfinity(Decimal32 value);
public static bool IsNormal(Decimal32 value);
public static bool IsOddInteger(Decimal32 value);
public static bool IsPositive(Decimal32 value);
public static bool IsPositiveInfinity(Decimal32 value);
public static bool IsRealNumber(Decimal32 value);
public static bool IsSubnormal(Decimal32 value);
static bool INumberBase<Decimal32>.IsZero(Decimal32 value); // Note: this ideally should be exposed implicitly as it is required by IEEE
public static Decimal32 MaxMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MaxMagnitudeNumber(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitude(Decimal32 x, Decimal32 y);
public static Decimal32 MinMagnitudeNumber(Decimal32 x, Decimal32 y);
static bool INumberBase<Decimal32>.TryConvertFromChecked<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromSaturating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertFromTruncating<TOther>(TOther value, out Decimal32 result);
static bool INumberBase<Decimal32>.TryConvertToChecked<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToSaturating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
static bool INumberBase<Decimal32>.TryConvertToTruncating<TOther>(Decimal32 value, [MaybeNullWhen(false)] out TOther result);
//
// IPowerFunctions
//
public static Decimal32 Pow(Decimal32 x, Decimal32 y); // PLATINUM
//
// IRootFunctions
//
public static Decimal32 Cbrt(Decimal32 x); // PLATINUM
public static Decimal32 Hypot(Decimal32 x, Decimal32 y); // PLATINUM
public static Decimal32 RootN(Decimal32 x, int n); // PLATINUM
public static Decimal32 Sqrt(Decimal32 x);
//
// ISignedNumber
//
public static Decimal32 NegativeOne { get; }
//
// ISubtractionOperators
//
public static Decimal32 operator -(Decimal32 left, Decimal32 right);
//
// ITrigonometricFunctions
//
public static Decimal32 Acos(Decimal32 x); // PLATINUM
public static Decimal32 AcosPi(Decimal32 x); // PLATINUM
public static Decimal32 Asin(Decimal32 x); // PLATINUM
public static Decimal32 AsinPi(Decimal32 x); // PLATINUM
public static Decimal32 Atan(Decimal32 x); // PLATINUM
public static Decimal32 AtanPi(Decimal32 x); // PLATINUM
public static Decimal32 Cos(Decimal32 x); // PLATINUM
public static Decimal32 CosPi(Decimal32 x); // PLATINUM
public static Decimal32 Sin(Decimal32 x); // PLATINUM
public static (Decimal32 Sin, Decimal32 Cos) SinCos(Decimal32 x); // PLATINUM
public static (Decimal32 SinPi, Decimal32 CosPi) SinCosPi(Decimal32 x); // PLATINUM
public static Decimal32 SinPi(Decimal32 x); // PLATINUM
public static Decimal32 Tan(Decimal32 x); // PLATINUM
public static Decimal32 TanPi(Decimal32 x); // PLATINUM
//
// IUnaryNegationOperators
//
public static Decimal32 operator -(Decimal32 value);
//
// IUnaryPlusOperators
//
public static Decimal32 operator +(Decimal32 value);
}
} |
Although the Decimal32, Decimal64, and Decimal128 APIs have been approved as shown above, we will not be able to finish the implementation, testing, and integration of these types during .NET 8. I'm moving this proposal to Future, and we will consider it during our .NET 9 planning. Note that this is not being marked as
help wanted
/cc @KTSnowy |
Hey @jeffhandley, in the meantime would it be alright if I used the same API described here for our own IEEE decimal library for C#? Does this API have any license attached to it? |
You can use the same license the code is under. |
@bartonjs I would recommend changing the UInt128 internal readonly ulong _upper;
internal readonly ulong _lower; This would allow easier marshalling of the Decimal128 struct to native code, because there's no guarantee that a C compiler has support for a Using UInt128 directly instead of two ulongs would make marshalling to C/C++ only available on compilers that support 128-bit integers. |
@KTSnowy I think those are mostly in the proposal to show that the type has internal fields (which makes the type ineligible for "C# Definite Assignment"). The actual implementation will probably take marshalling into account. But, good observation. |
Hi!, we're looking forward to being able to use these new decimal data types. We are building a financial application that needs to handle a big numbers (it can be trillions) and also needs to support a high number of decimals, making |
|
Hi @tannergooding @jeffhandley is it ok for me to work on this, I intend to implement a part of it (without PLATINUM api). |
@RaymondHuy You are free to start working on this effort as it has been marked "approved". I would suggest the following to ensure a productive experience.
|
Please note that while this particular issue is fine to pick up, an issue simply being marked "approved" does not mean we would take any PR on the feature. It is at the discretion of the individual area owners and we have explicit guidance specifically targeted towards "big" features like this one asking for coordination with the area owners: https://github.com/dotnet/runtime/blob/main/CONTRIBUTING.md In an ideal scenario, the process is typically:
In this particular case, there is a known good implementation by Intel that we would like to port to .NET: https://www.netlib.org/misc/intel/ -- It is made available under the BSD 3-Clause License We can do this work "incrementally" in that there is:
The prioritization of types would be There are notably two formats that these decimal types can be encoded as:
Given we are implementing this in software, following the binary encoding is desirable and is what the Intel based implementation is centered around. The Intel implementation also uses some fairly large tables in its implementation. Depending on the total impact, we may end up wanting to deviate from that to save space at the cost of some performance. It is something that will need to be measured and decided upon. If you would still like to pick up the issue, please let me know and I can assign it out. Myself and the other area owners ( @dotnet/area-system-numerics ) will be available to answer questions and help you through the process as needed. |
For context, according to LLVMs discourse, GCC and Clang do Binary on XArch and Decimal on other platforms for the C23
|
That would be incorrect. Arm explicitly documents that decimal support is done using the binary based encoding: https://github.com/ARM-software/abi-aa/blob/2982a9f3b512a5bfdc9e3fea5d3b298f9165c36b/aapcs64/aapcs64.rst#decimal-floating-point
The same goes for the x86-64 SysV ABI specification (used by Linux systems): https://gitlab.com/x86-psABIs/x86-64-ABI
The intent of the specification is that any software based implementation is done using the binary encoding where it is more efficient. There is no need for our software based support to then support anything except for the binary based encoding as no platforms should be using it if they provide hardware support, where the decimal based encoding should be used instead and the APIs would be handled instrinsically. |
Thanks for your replies, I have been contributing to System.Text.Json area so the process sounds familiar with me. I intend to start like your suggestion @tannergooding , you can assign me this issue. 😉 |
Hi @tannergooding I wonder about this case:
As you see the |
IEEE 754 requires that inputs are taken as given, computed as if to infinite precision and unbounded range, and then rounded to the nearest representable result. Thus |
Background and motivation
This is a restructuring of the original API proposal here: #69777
The existing
System.Decimal
type does not conform to the IEEE standard for decimal floating-point types. We have no plans to rehashSystem.Decimal
, but addingDecimal32
,Decimal64
, andDecimal128
in addition would allow users to work within a standard that is being adopted by other languages and frameworks. There is also a future where hardware support for these types is more widely adopted, and having IEEE-conforming types will allow us to users to take advantage of performance gains.For reference, here is a chart comparing the existing System.Decimal type to the IEEE types:
Alternative Designs
PLATINUM
. These APIs represent high-implementation-cost functionality that is only "recommended" by IEEE. These are APIs that we might want eventually, as implementing all of them will allow these types to inherit fromIFloatingPointIeee754
instead of justIFloatingPoint
. Given the implementation cost, we are targeting shipping a smaller surface for .NET 8 that does not include thePLATINUM
APIs..123m
literal syntax (because it represents a superset of System.Decimal), but otherwise the only real way to accurately create these types is via parsing. EDIT: yes, added constructorRisks/Considerations
Decimal128
, as the current lack of hardware support will require the below APIs to be implemented in software.System.Decimal
. We are mitigating this by placing these types inSystem.Numerics
. Clear documentation will be required.API Proposal
Decimal32
is shown, but the API surfaces forDecimal64
andDecimal128
are nearly identical, bar some differences in the conversions (which are noted below). This proposal is for all three types.The text was updated successfully, but these errors were encountered: