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 465052fd4a9eb5..8c7c95bfdbf1ad 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -597,6 +597,18 @@ private static TValue PropagateNaN(TValue left, TValue right) return (nanBits & TDecimal.SignMask) | TDecimal.NaNMask | payload; } + /// + /// Returns unchanged when it is a number, or the canonical quiet NaN when it is a + /// NaN. The minimum/maximum family selects one operand to return; routing that operand through this helper + /// canonicalizes a NaN result as IEEE 754-2019 §5.1 requires without disturbing the numeric selection. + /// + private static TValue CanonicalizeIfNaN(TValue bits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + return TDecimal.IsNaN(bits) ? PropagateNaN(bits, bits) : bits; + } + /// /// Adds two IEEE 754 decimal values represented by their raw bit patterns and returns the /// bit pattern of the correctly rounded (round-to-nearest, ties-to-even) sum. @@ -1015,6 +1027,218 @@ internal static TValue DivideDecimalIeee754(TValue left, TValu return NumberToDecimalIeee754BitsFromWide(resultSign, TValue.Zero, quotient, quotientExponent, remainderNonZero); } + /// + /// Computes the truncated remainder (the % operator) of two IEEE 754 decimal values represented by their + /// raw bit patterns: x - Truncate(x / y) * y, matching the C# floating-point % operator on + /// // (and not the round-to-nearest + /// IEEE 754 remainder operation). + /// + /// + /// The result carries the sign of the dividend, has magnitude strictly less than |y|, and is always + /// exact. It is computed at the IEEE 754 preferred exponent min(exp(x), exp(y)) by reducing the + /// dividend coefficient modulo the divisor coefficient. Every intermediate value stays within a single limb: + /// the running remainder is always below the divisor, so each step scales it up by as many trailing zeros as + /// keep the product within the integer width (capped at the largest cached power of ten) before taking the + /// remainder again, stopping once the remainder reaches zero. This mirrors the behavior of the Intel reference + /// implementation. + /// + internal static TValue RemainderDecimalIeee754(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger, IMinMaxValue + { + // This code is based on `bid32_fmod`, `bid64_fmod`, and `bid128_fmod` from Intel(R) Decimal Floating-Point Math Library + // Copyright (c) 2007-2025, Intel Corp. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + if (TDecimal.IsNaN(left) || TDecimal.IsNaN(right)) + { + return PropagateNaN(left, right); + } + + // The remainder always carries the sign of the dividend. + bool resultSign = TDecimal.IsNegative(left); + + if (TDecimal.IsInfinity(left)) + { + // Infinity has no finite remainder; the operation is invalid and produces the canonical quiet NaN. + return TDecimal.NaNMask; + } + + DecodedDecimalIeee754 a = UnpackDecimalIeee754(left); + + if (TDecimal.IsInfinity(right)) + { + // A finite value has itself as its remainder modulo infinity; re-encode to a canonical form. + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, a.Significand, a.UnbiasedExponent); + } + + DecodedDecimalIeee754 b = UnpackDecimalIeee754(right); + + if (TValue.IsZero(b.Significand)) + { + // A remainder with a zero divisor is invalid and produces the canonical quiet NaN. + return TDecimal.NaNMask; + } + + // The preferred exponent of the remainder is the smaller of the two operand exponents. + int resultExponent = Math.Min(a.UnbiasedExponent, b.UnbiasedExponent); + + if (TValue.IsZero(a.Significand)) + { + // Zero modulo any non-zero value is a signed zero at the preferred exponent. + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, TValue.Zero, resultExponent); + } + + TValue remainder; + + if (a.UnbiasedExponent >= b.UnbiasedExponent) + { + // Reduce `a.Significand * 10^(ea - eb)` modulo `b.Significand`. The remainder always stays below the + // divisor, so `remainder * 10^chunk` fits the integer width as long as `10^chunk <= MaxValue / divisor`. + // Fold that many trailing zeros per step (capped at the largest cached power of ten). Modular + // arithmetic lets each step absorb several digits at once, so a small divisor reaches the full cached + // power while a large one folds only a handful. Once the remainder hits zero it stays zero, so stop. + // + // TODO: A wider intermediate (as Intel's `bidNN_fmod` uses for Decimal128) would let every step fold + // the full `Precision` digits regardless of divisor magnitude, shaving the loop count for large gaps. + remainder = a.Significand % b.Significand; + + int chunk = 1; + TValue chunkLimit = TValue.MaxValue / b.Significand; + + while ((chunk < TDecimal.Precision - 1) && (TDecimal.Power10(chunk + 1) <= chunkLimit)) + { + chunk++; + } + + for (int gap = a.UnbiasedExponent - b.UnbiasedExponent; (gap > 0) && !TValue.IsZero(remainder); gap -= chunk) + { + int step = Math.Min(chunk, gap); + remainder = (remainder * TDecimal.Power10(step)) % b.Significand; + } + } + else + { + // The divisor's coefficient is scaled up by `10^(eb - ea)`. When that scaled divisor cannot fit the + // format precision it necessarily exceeds the dividend coefficient, so the dividend is the remainder. + int gap = b.UnbiasedExponent - a.UnbiasedExponent; + + if (TDecimal.CountDigits(b.Significand) + gap > TDecimal.Precision) + { + remainder = a.Significand; + } + else + { + remainder = a.Significand % (b.Significand * TDecimal.Power10(gap)); + } + } + + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, remainder, resultExponent); + } + + /// + /// Rounds a finite value to fractional digits under . + /// The value is coefficient * 10^exponent; when its quantum exponent is already at or above + /// -digits there is nothing to round and it is returned unchanged. Otherwise the digits below + /// 10^(-digits) are discarded and the retained coefficient is incremented per . + /// Every intermediate fits a single limb: the divisor is at most 10^(Precision - 1) and dividing by + /// at least ten keeps the rounded coefficient below MaxSignificand. + /// + internal static TValue RoundDecimalIeee754(TValue bits, int digits, MidpointRounding mode) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + if (digits < 0) + { + ThrowHelper.ThrowArgumentOutOfRange_RoundingDigits(nameof(digits)); + } + + if ((uint)mode > (uint)MidpointRounding.ToPositiveInfinity) + { + ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); + } + + if (TDecimal.IsNaN(bits)) + { + // Canonicalize so a signaling or out-of-range-payload NaN operand rounds to the canonical quiet NaN. + return PropagateNaN(bits, bits); + } + + if (TDecimal.IsInfinity(bits)) + { + // Canonicalize so a non-canonical infinity operand rounds to the canonical infinity. + return TDecimal.IsNegative(bits) ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + DecodedDecimalIeee754 a = UnpackDecimalIeee754(bits); + int targetExponent = -digits; + + if (a.UnbiasedExponent >= targetExponent) + { + // The quantum is already at or coarser than the requested precision; nothing is discarded. Re-encode so + // a non-canonical operand (coefficient above the format maximum, unpacked to zero) is returned canonical. + return DecimalIeee754FiniteNumberBinaryEncoding(a.Signed, a.Significand, a.UnbiasedExponent); + } + + int drop = targetExponent - a.UnbiasedExponent; + int coefficientDigits = TValue.IsZero(a.Significand) ? 0 : TDecimal.CountDigits(a.Significand); + + TValue five = TValue.CreateTruncating(5); + TValue quotient; + + // Sign of (discarded - half quantum): negative below the midpoint, zero at it, positive above it. + int discardedComparedToHalf; + bool discardedNonZero; + + if (drop >= coefficientDigits) + { + // Every coefficient digit is discarded, so the retained value is zero before rounding. + quotient = TValue.Zero; + + if (drop > coefficientDigits) + { + // The whole coefficient is strictly less than half of the discarded quantum. + discardedComparedToHalf = -1; + discardedNonZero = !TValue.IsZero(a.Significand); + } + else + { + TValue half = five * TDecimal.Power10(coefficientDigits - 1); + discardedComparedToHalf = a.Significand.CompareTo(half); + discardedNonZero = true; + } + } + else + { + TValue divisor = TDecimal.Power10(drop); + TValue discarded; + (quotient, discarded) = TValue.DivRem(a.Significand, divisor); + + TValue half = five * TDecimal.Power10(drop - 1); + discardedComparedToHalf = discarded.CompareTo(half); + discardedNonZero = !TValue.IsZero(discarded); + } + + bool roundAwayFromZero = mode switch + { + MidpointRounding.ToEven => (discardedComparedToHalf > 0) || ((discardedComparedToHalf == 0) && !TValue.IsZero(quotient & TValue.One)), + MidpointRounding.AwayFromZero => discardedComparedToHalf >= 0, + MidpointRounding.ToZero => false, + MidpointRounding.ToNegativeInfinity => discardedNonZero && a.Signed, + MidpointRounding.ToPositiveInfinity => discardedNonZero && !a.Signed, + _ => throw new UnreachableException(), + }; + + if (roundAwayFromZero) + { + quotient += TValue.One; + } + + return DecimalIeee754FiniteNumberBinaryEncoding(a.Signed, quotient, targetExponent); + } + /// /// Computes the scale factor 10^ used to align addition operands. Exponents /// within the format's Power10 lookup range (0..Precision - 1) come straight from that table, @@ -1575,7 +1799,7 @@ internal static TValue MaxMagnitudeDecimalIeee754(TValue x, TV if (GreaterThanDecimalIeee754(ax, ay) || TDecimal.IsNaN(ax)) { - return x; + return CanonicalizeIfNaN(x); } if (EqualsDecimalIeee754(ax, ay)) @@ -1583,7 +1807,7 @@ internal static TValue MaxMagnitudeDecimalIeee754(TValue x, TV return TDecimal.IsNegative(x) ? y : x; } - return y; + return CanonicalizeIfNaN(y); } internal static TValue MinMagnitudeDecimalIeee754(TValue x, TValue y) @@ -1595,7 +1819,7 @@ internal static TValue MinMagnitudeDecimalIeee754(TValue x, TV if (LessThanDecimalIeee754(ax, ay) || TDecimal.IsNaN(ax)) { - return x; + return CanonicalizeIfNaN(x); } if (EqualsDecimalIeee754(ax, ay)) @@ -1603,7 +1827,7 @@ internal static TValue MinMagnitudeDecimalIeee754(TValue x, TV return TDecimal.IsNegative(x) ? x : y; } - return y; + return CanonicalizeIfNaN(y); } internal static TValue MaxMagnitudeNumberDecimalIeee754(TValue x, TValue y) @@ -1615,7 +1839,7 @@ internal static TValue MaxMagnitudeNumberDecimalIeee754(TValue if (GreaterThanDecimalIeee754(ax, ay) || TDecimal.IsNaN(ay)) { - return x; + return CanonicalizeIfNaN(x); } if (EqualsDecimalIeee754(ax, ay)) @@ -1635,7 +1859,7 @@ internal static TValue MinMagnitudeNumberDecimalIeee754(TValue if (LessThanDecimalIeee754(ax, ay) || TDecimal.IsNaN(ay)) { - return x; + return CanonicalizeIfNaN(x); } if (EqualsDecimalIeee754(ax, ay)) @@ -1656,12 +1880,12 @@ internal static TValue MaxDecimalIeee754(TValue x, TValue y) { if (TDecimal.IsNaN(x)) { - return x; + return PropagateNaN(x, x); } if (TDecimal.IsNaN(y)) { - return y; + return PropagateNaN(y, y); } if (!EqualsDecimalIeee754(x, y)) @@ -1678,12 +1902,12 @@ internal static TValue MinDecimalIeee754(TValue x, TValue y) { if (TDecimal.IsNaN(x)) { - return x; + return PropagateNaN(x, x); } if (TDecimal.IsNaN(y)) { - return y; + return PropagateNaN(y, y); } if (!EqualsDecimalIeee754(x, y)) @@ -1698,14 +1922,14 @@ internal static TValue MaxNativeDecimalIeee754(TValue x, TValu where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo where TValue : unmanaged, IBinaryInteger { - return GreaterThanDecimalIeee754(x, y) ? x : y; + return CanonicalizeIfNaN(GreaterThanDecimalIeee754(x, y) ? x : y); } internal static TValue MinNativeDecimalIeee754(TValue x, TValue y) where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo where TValue : unmanaged, IBinaryInteger { - return LessThanDecimalIeee754(x, y) ? x : y; + return CanonicalizeIfNaN(LessThanDecimalIeee754(x, y) ? x : y); } internal static TValue MaxNumberDecimalIeee754(TValue x, TValue y) @@ -1719,7 +1943,7 @@ internal static TValue MaxNumberDecimalIeee754(TValue x, TValu return LessThanDecimalIeee754(y, x) ? x : y; } - return x; + return CanonicalizeIfNaN(x); } return TDecimal.IsNegative(y) ? x : y; @@ -1736,7 +1960,7 @@ internal static TValue MinNumberDecimalIeee754(TValue x, TValu return LessThanDecimalIeee754(x, y) ? x : y; } - return x; + return CanonicalizeIfNaN(x); } return TDecimal.IsNegative(x) ? x : y; 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 ecb10988e1c726..2bfa96b4ce84af 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -1,6 +1,7 @@ // 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.Binary; using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -12,7 +13,7 @@ public readonly struct Decimal128 : IComparable, IComparable, IEquatable, - INumberBase, + IFloatingPoint, ISpanFormattable, ISpanParsable, IMinMaxValue, @@ -338,6 +339,16 @@ public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringS return new Decimal128(result); } + /// Divides two values together to compute their remainder. + /// The value which divides. + /// The value which divides . + /// The remainder of divided by . + public static Decimal128 operator %(Decimal128 left, Decimal128 right) + { + UInt128 result = Number.RemainderDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + return new Decimal128(result); + } + // // Explicit conversions to Decimal128 // @@ -732,6 +743,115 @@ public static Decimal128 Parse(ReadOnlySpan utf8Text, NumberStyles style = /// Gets the mathematical constant tau. public static Decimal128 Tau => new Decimal128(TauValue); + // + // IFloatingPoint + // + + /// + public static Decimal128 Ceiling(Decimal128 x) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), 0, MidpointRounding.ToPositiveInfinity)); + + /// + public static TInteger ConvertToInteger(Decimal128 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static TInteger ConvertToIntegerNative(Decimal128 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static Decimal128 Floor(Decimal128 x) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), 0, MidpointRounding.ToNegativeInfinity)); + + /// + public static Decimal128 Round(Decimal128 x) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), 0, MidpointRounding.ToEven)); + + /// + public static Decimal128 Round(Decimal128 x, int digits) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), digits, MidpointRounding.ToEven)); + + /// + public static Decimal128 Round(Decimal128 x, MidpointRounding mode) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), 0, mode)); + + /// + public static Decimal128 Round(Decimal128 x, int digits, MidpointRounding mode) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), digits, mode)); + + /// + public static Decimal128 Truncate(Decimal128 x) => new Decimal128(Number.RoundDecimalIeee754(new UInt128(x._upper, x._lower), 0, MidpointRounding.ToZero)); + + /// + int IFloatingPoint.GetExponentByteCount() => sizeof(int); + + /// + int IFloatingPoint.GetExponentShortestBitLength() + { + int exponent = Number.UnpackDecimalIeee754(new UInt128(_upper, _lower)).UnbiasedExponent; + + if (exponent >= 0) + { + return (sizeof(int) * 8) - int.LeadingZeroCount(exponent); + } + else + { + return (sizeof(int) * 8) + 1 - int.LeadingZeroCount(~exponent); + } + } + + /// + int IFloatingPoint.GetSignificandBitLength() => 113; + + /// + int IFloatingPoint.GetSignificandByteCount() => Unsafe.SizeOf(); + + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32BigEndian(destination, Number.UnpackDecimalIeee754(new UInt128(_upper, _lower)).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32LittleEndian(destination, Number.UnpackDecimalIeee754(new UInt128(_upper, _lower)).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt128BigEndian(destination, Number.UnpackDecimalIeee754(new UInt128(_upper, _lower)).Significand)) + { + bytesWritten = Unsafe.SizeOf(); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt128LittleEndian(destination, Number.UnpackDecimalIeee754(new UInt128(_upper, _lower)).Significand)) + { + bytesWritten = Unsafe.SizeOf(); + return true; + } + + bytesWritten = 0; + return false; + } + /// Computes the absolute of a value. /// The value for which to get its absolute. /// The absolute of . 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 9b4d66fc367434..8000356c12d92b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -1,6 +1,7 @@ // 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.Binary; using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -14,7 +15,7 @@ public readonly struct Decimal32 : IComparable, IComparable, IEquatable, - INumberBase, + IFloatingPoint, ISpanFormattable, ISpanParsable, IMinMaxValue, @@ -338,6 +339,15 @@ public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringS return new Decimal32(Number.DivideDecimalIeee754(left._value, right._value)); } + /// Divides two values together to compute their remainder. + /// The value which divides. + /// The value which divides . + /// The remainder of divided by . + public static Decimal32 operator %(Decimal32 left, Decimal32 right) + { + return new Decimal32(Number.RemainderDecimalIeee754(left._value, right._value)); + } + // // Explicit conversions to Decimal32 // @@ -756,6 +766,115 @@ public static Decimal32 Parse(ReadOnlySpan utf8Text, NumberStyles style = /// Gets the mathematical constant tau. public static Decimal32 Tau => new Decimal32(TauValue); + // + // IFloatingPoint + // + + /// + public static Decimal32 Ceiling(Decimal32 x) => new Decimal32(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToPositiveInfinity)); + + /// + public static TInteger ConvertToInteger(Decimal32 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static TInteger ConvertToIntegerNative(Decimal32 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static Decimal32 Floor(Decimal32 x) => new Decimal32(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToNegativeInfinity)); + + /// + public static Decimal32 Round(Decimal32 x) => new Decimal32(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToEven)); + + /// + public static Decimal32 Round(Decimal32 x, int digits) => new Decimal32(Number.RoundDecimalIeee754(x._value, digits, MidpointRounding.ToEven)); + + /// + public static Decimal32 Round(Decimal32 x, MidpointRounding mode) => new Decimal32(Number.RoundDecimalIeee754(x._value, 0, mode)); + + /// + public static Decimal32 Round(Decimal32 x, int digits, MidpointRounding mode) => new Decimal32(Number.RoundDecimalIeee754(x._value, digits, mode)); + + /// + public static Decimal32 Truncate(Decimal32 x) => new Decimal32(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToZero)); + + /// + int IFloatingPoint.GetExponentByteCount() => sizeof(int); + + /// + int IFloatingPoint.GetExponentShortestBitLength() + { + int exponent = Number.UnpackDecimalIeee754(_value).UnbiasedExponent; + + if (exponent >= 0) + { + return (sizeof(int) * 8) - int.LeadingZeroCount(exponent); + } + else + { + return (sizeof(int) * 8) + 1 - int.LeadingZeroCount(~exponent); + } + } + + /// + int IFloatingPoint.GetSignificandBitLength() => 24; + + /// + int IFloatingPoint.GetSignificandByteCount() => sizeof(uint); + + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32BigEndian(destination, Number.UnpackDecimalIeee754(_value).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32LittleEndian(destination, Number.UnpackDecimalIeee754(_value).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt32BigEndian(destination, Number.UnpackDecimalIeee754(_value).Significand)) + { + bytesWritten = sizeof(uint); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt32LittleEndian(destination, Number.UnpackDecimalIeee754(_value).Significand)) + { + bytesWritten = sizeof(uint); + return true; + } + + bytesWritten = 0; + return false; + } + /// Computes the absolute of a value. /// The value for which to get its absolute. /// The absolute of . 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 8cb058bdb819ac..1b08a963722056 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -1,6 +1,7 @@ // 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.Binary; using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -12,7 +13,7 @@ public readonly struct Decimal64 : IComparable, IComparable, IEquatable, - INumberBase, + IFloatingPoint, ISpanFormattable, ISpanParsable, IMinMaxValue, @@ -339,6 +340,15 @@ public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringS return new Decimal64(Number.DivideDecimalIeee754(left._value, right._value)); } + /// Divides two values together to compute their remainder. + /// The value which divides. + /// The value which divides . + /// The remainder of divided by . + public static Decimal64 operator %(Decimal64 left, Decimal64 right) + { + return new Decimal64(Number.RemainderDecimalIeee754(left._value, right._value)); + } + // // Explicit conversions to Decimal64 // @@ -747,6 +757,115 @@ public static Decimal64 Parse(ReadOnlySpan utf8Text, NumberStyles style = /// Gets the mathematical constant tau. public static Decimal64 Tau => new Decimal64(TauValue); + // + // IFloatingPoint + // + + /// + public static Decimal64 Ceiling(Decimal64 x) => new Decimal64(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToPositiveInfinity)); + + /// + public static TInteger ConvertToInteger(Decimal64 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static TInteger ConvertToIntegerNative(Decimal64 value) + where TInteger : IBinaryInteger => TInteger.CreateSaturating(value); + + /// + public static Decimal64 Floor(Decimal64 x) => new Decimal64(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToNegativeInfinity)); + + /// + public static Decimal64 Round(Decimal64 x) => new Decimal64(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToEven)); + + /// + public static Decimal64 Round(Decimal64 x, int digits) => new Decimal64(Number.RoundDecimalIeee754(x._value, digits, MidpointRounding.ToEven)); + + /// + public static Decimal64 Round(Decimal64 x, MidpointRounding mode) => new Decimal64(Number.RoundDecimalIeee754(x._value, 0, mode)); + + /// + public static Decimal64 Round(Decimal64 x, int digits, MidpointRounding mode) => new Decimal64(Number.RoundDecimalIeee754(x._value, digits, mode)); + + /// + public static Decimal64 Truncate(Decimal64 x) => new Decimal64(Number.RoundDecimalIeee754(x._value, 0, MidpointRounding.ToZero)); + + /// + int IFloatingPoint.GetExponentByteCount() => sizeof(int); + + /// + int IFloatingPoint.GetExponentShortestBitLength() + { + int exponent = Number.UnpackDecimalIeee754(_value).UnbiasedExponent; + + if (exponent >= 0) + { + return (sizeof(int) * 8) - int.LeadingZeroCount(exponent); + } + else + { + return (sizeof(int) * 8) + 1 - int.LeadingZeroCount(~exponent); + } + } + + /// + int IFloatingPoint.GetSignificandBitLength() => 54; + + /// + int IFloatingPoint.GetSignificandByteCount() => sizeof(ulong); + + /// + bool IFloatingPoint.TryWriteExponentBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32BigEndian(destination, Number.UnpackDecimalIeee754(_value).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteExponentLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteInt32LittleEndian(destination, Number.UnpackDecimalIeee754(_value).UnbiasedExponent)) + { + bytesWritten = sizeof(int); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandBigEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt64BigEndian(destination, Number.UnpackDecimalIeee754(_value).Significand)) + { + bytesWritten = sizeof(ulong); + return true; + } + + bytesWritten = 0; + return false; + } + + /// + bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destination, out int bytesWritten) + { + if (BinaryPrimitives.TryWriteUInt64LittleEndian(destination, Number.UnpackDecimalIeee754(_value).Significand)) + { + bytesWritten = sizeof(ulong); + return true; + } + + bytesWritten = 0; + return false; + } + /// Computes the absolute of a value. /// The value for which to get its absolute. /// The absolute of . diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 7cee29cf852df5..21f8d460f3f0fb 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11429,7 +11429,7 @@ public static void HtmlEncode(string? value, System.IO.TextWriter output) { } } namespace System.Numerics { - public readonly partial struct Decimal128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumberBase, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Decimal128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { private readonly int _dummyPrimitive; public static System.Numerics.Decimal128 E { get { throw null; } } @@ -11449,16 +11449,20 @@ namespace System.Numerics public static System.Numerics.Decimal128 Tau { get { throw null; } } public static System.Numerics.Decimal128 Zero { get { throw null; } } public static System.Numerics.Decimal128 Abs(System.Numerics.Decimal128 value) { throw null; } + public static System.Numerics.Decimal128 Ceiling(System.Numerics.Decimal128 x) { throw null; } public static System.Numerics.Decimal128 Clamp(System.Numerics.Decimal128 value, System.Numerics.Decimal128 min, System.Numerics.Decimal128 max) { throw null; } public static System.Numerics.Decimal128 ClampNative(System.Numerics.Decimal128 value, System.Numerics.Decimal128 min, System.Numerics.Decimal128 max) { throw null; } public int CompareTo(System.Numerics.Decimal128 other) { throw null; } public int CompareTo(object? value) { throw null; } + public static TInteger ConvertToIntegerNative(System.Numerics.Decimal128 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } + public static TInteger ConvertToInteger(System.Numerics.Decimal128 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Decimal128 CopySign(System.Numerics.Decimal128 value, System.Numerics.Decimal128 sign) { throw null; } public static System.Numerics.Decimal128 CreateChecked(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal128 CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal128 CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public bool Equals(System.Numerics.Decimal128 other) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } + public static System.Numerics.Decimal128 Floor(System.Numerics.Decimal128 x) { throw null; } public override int GetHashCode() { throw null; } public static bool IsEvenInteger(System.Numerics.Decimal128 value) { throw null; } public static bool IsFinite(System.Numerics.Decimal128 value) { throw null; } @@ -11559,6 +11563,7 @@ namespace System.Numerics public static bool operator !=(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } public static bool operator <(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } public static bool operator <=(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } + public static System.Numerics.Decimal128 operator %(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } public static System.Numerics.Decimal128 operator *(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } public static System.Numerics.Decimal128 operator -(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } public static System.Numerics.Decimal128 operator -(System.Numerics.Decimal128 value) { throw null; } @@ -11571,7 +11576,19 @@ namespace System.Numerics public static System.Numerics.Decimal128 Parse(string s, System.Globalization.NumberStyles style) { throw null; } public static System.Numerics.Decimal128 Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } public static System.Numerics.Decimal128 Parse(string s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal128 Round(System.Numerics.Decimal128 x) { throw null; } + public static System.Numerics.Decimal128 Round(System.Numerics.Decimal128 x, int digits) { throw null; } + public static System.Numerics.Decimal128 Round(System.Numerics.Decimal128 x, int digits, System.MidpointRounding mode) { throw null; } + public static System.Numerics.Decimal128 Round(System.Numerics.Decimal128 x, System.MidpointRounding mode) { throw null; } public static int Sign(System.Numerics.Decimal128 value) { throw null; } + int System.Numerics.IFloatingPoint.GetExponentByteCount() { throw null; } + int System.Numerics.IFloatingPoint.GetExponentShortestBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandByteCount() { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandLittleEndian(System.Span destination, out int bytesWritten) { throw null; } static bool System.Numerics.INumberBase.IsCanonical(System.Numerics.Decimal128 value) { throw null; } static bool System.Numerics.INumberBase.IsComplexNumber(System.Numerics.Decimal128 value) { throw null; } static bool System.Numerics.INumberBase.IsImaginaryNumber(System.Numerics.Decimal128 value) { throw null; } @@ -11586,6 +11603,7 @@ namespace System.Numerics public string ToString(System.IFormatProvider? provider) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal128 Truncate(System.Numerics.Decimal128 x) { throw null; } public bool TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public static bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal128 result) { throw null; } @@ -11601,7 +11619,7 @@ namespace System.Numerics public static bool TryParsePartial([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int charsConsumed) { throw null; } } - public readonly partial struct Decimal32 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumberBase, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Decimal32 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { private readonly int _dummyPrimitive; public static System.Numerics.Decimal32 E { get { throw null; } } @@ -11621,16 +11639,20 @@ namespace System.Numerics public static System.Numerics.Decimal32 Tau { get { throw null; } } public static System.Numerics.Decimal32 Zero { get { throw null; } } public static System.Numerics.Decimal32 Abs(System.Numerics.Decimal32 value) { throw null; } + public static System.Numerics.Decimal32 Ceiling(System.Numerics.Decimal32 x) { throw null; } public static System.Numerics.Decimal32 Clamp(System.Numerics.Decimal32 value, System.Numerics.Decimal32 min, System.Numerics.Decimal32 max) { throw null; } public static System.Numerics.Decimal32 ClampNative(System.Numerics.Decimal32 value, System.Numerics.Decimal32 min, System.Numerics.Decimal32 max) { throw null; } public int CompareTo(System.Numerics.Decimal32 other) { throw null; } public int CompareTo(object? value) { throw null; } + public static TInteger ConvertToIntegerNative(System.Numerics.Decimal32 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } + public static TInteger ConvertToInteger(System.Numerics.Decimal32 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Decimal32 CopySign(System.Numerics.Decimal32 value, System.Numerics.Decimal32 sign) { throw null; } public static System.Numerics.Decimal32 CreateChecked(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal32 CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal32 CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public bool Equals(System.Numerics.Decimal32 other) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } + public static System.Numerics.Decimal32 Floor(System.Numerics.Decimal32 x) { throw null; } public override int GetHashCode() { throw null; } public static bool IsEvenInteger(System.Numerics.Decimal32 value) { throw null; } public static bool IsFinite(System.Numerics.Decimal32 value) { throw null; } @@ -11735,6 +11757,7 @@ namespace System.Numerics public static bool operator !=(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } public static bool operator <(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } public static bool operator <=(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } + public static System.Numerics.Decimal32 operator %(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } public static System.Numerics.Decimal32 operator *(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } public static System.Numerics.Decimal32 operator -(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } public static System.Numerics.Decimal32 operator -(System.Numerics.Decimal32 value) { throw null; } @@ -11747,7 +11770,19 @@ namespace System.Numerics public static System.Numerics.Decimal32 Parse(string s, System.Globalization.NumberStyles style) { throw null; } public static System.Numerics.Decimal32 Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } public static System.Numerics.Decimal32 Parse(string s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal32 Round(System.Numerics.Decimal32 x) { throw null; } + public static System.Numerics.Decimal32 Round(System.Numerics.Decimal32 x, int digits) { throw null; } + public static System.Numerics.Decimal32 Round(System.Numerics.Decimal32 x, int digits, System.MidpointRounding mode) { throw null; } + public static System.Numerics.Decimal32 Round(System.Numerics.Decimal32 x, System.MidpointRounding mode) { throw null; } public static int Sign(System.Numerics.Decimal32 value) { throw null; } + int System.Numerics.IFloatingPoint.GetExponentByteCount() { throw null; } + int System.Numerics.IFloatingPoint.GetExponentShortestBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandByteCount() { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandLittleEndian(System.Span destination, out int bytesWritten) { throw null; } static bool System.Numerics.INumberBase.IsCanonical(System.Numerics.Decimal32 value) { throw null; } static bool System.Numerics.INumberBase.IsComplexNumber(System.Numerics.Decimal32 value) { throw null; } static bool System.Numerics.INumberBase.IsImaginaryNumber(System.Numerics.Decimal32 value) { throw null; } @@ -11762,6 +11797,7 @@ namespace System.Numerics public string ToString(System.IFormatProvider? provider) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal32 Truncate(System.Numerics.Decimal32 x) { throw null; } public bool TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public static bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal32 result) { throw null; } @@ -11777,7 +11813,7 @@ namespace System.Numerics public static bool TryParsePartial([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int charsConsumed) { throw null; } } - public readonly partial struct Decimal64 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumberBase, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + public readonly partial struct Decimal64 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { private readonly int _dummyPrimitive; public static System.Numerics.Decimal64 E { get { throw null; } } @@ -11797,16 +11833,20 @@ namespace System.Numerics public static System.Numerics.Decimal64 Tau { get { throw null; } } public static System.Numerics.Decimal64 Zero { get { throw null; } } public static System.Numerics.Decimal64 Abs(System.Numerics.Decimal64 value) { throw null; } + public static System.Numerics.Decimal64 Ceiling(System.Numerics.Decimal64 x) { throw null; } public static System.Numerics.Decimal64 Clamp(System.Numerics.Decimal64 value, System.Numerics.Decimal64 min, System.Numerics.Decimal64 max) { throw null; } public static System.Numerics.Decimal64 ClampNative(System.Numerics.Decimal64 value, System.Numerics.Decimal64 min, System.Numerics.Decimal64 max) { throw null; } public int CompareTo(System.Numerics.Decimal64 other) { throw null; } public int CompareTo(object? value) { throw null; } + public static TInteger ConvertToIntegerNative(System.Numerics.Decimal64 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } + public static TInteger ConvertToInteger(System.Numerics.Decimal64 value) where TInteger : System.Numerics.IBinaryInteger { throw null; } public static System.Numerics.Decimal64 CopySign(System.Numerics.Decimal64 value, System.Numerics.Decimal64 sign) { throw null; } public static System.Numerics.Decimal64 CreateChecked(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal64 CreateSaturating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public static System.Numerics.Decimal64 CreateTruncating(TOther value) where TOther : System.Numerics.INumberBase { throw null; } public bool Equals(System.Numerics.Decimal64 other) { throw null; } public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; } + public static System.Numerics.Decimal64 Floor(System.Numerics.Decimal64 x) { throw null; } public override int GetHashCode() { throw null; } public static bool IsEvenInteger(System.Numerics.Decimal64 value) { throw null; } public static bool IsFinite(System.Numerics.Decimal64 value) { throw null; } @@ -11909,6 +11949,7 @@ namespace System.Numerics public static bool operator !=(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } public static bool operator <(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } public static bool operator <=(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } + public static System.Numerics.Decimal64 operator %(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } public static System.Numerics.Decimal64 operator *(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } public static System.Numerics.Decimal64 operator -(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } public static System.Numerics.Decimal64 operator -(System.Numerics.Decimal64 value) { throw null; } @@ -11921,7 +11962,19 @@ namespace System.Numerics public static System.Numerics.Decimal64 Parse(string s, System.Globalization.NumberStyles style) { throw null; } public static System.Numerics.Decimal64 Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; } public static System.Numerics.Decimal64 Parse(string s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal64 Round(System.Numerics.Decimal64 x) { throw null; } + public static System.Numerics.Decimal64 Round(System.Numerics.Decimal64 x, int digits) { throw null; } + public static System.Numerics.Decimal64 Round(System.Numerics.Decimal64 x, int digits, System.MidpointRounding mode) { throw null; } + public static System.Numerics.Decimal64 Round(System.Numerics.Decimal64 x, System.MidpointRounding mode) { throw null; } public static int Sign(System.Numerics.Decimal64 value) { throw null; } + int System.Numerics.IFloatingPoint.GetExponentByteCount() { throw null; } + int System.Numerics.IFloatingPoint.GetExponentShortestBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandBitLength() { throw null; } + int System.Numerics.IFloatingPoint.GetSignificandByteCount() { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteExponentLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandBigEndian(System.Span destination, out int bytesWritten) { throw null; } + bool System.Numerics.IFloatingPoint.TryWriteSignificandLittleEndian(System.Span destination, out int bytesWritten) { throw null; } static bool System.Numerics.INumberBase.IsCanonical(System.Numerics.Decimal64 value) { throw null; } static bool System.Numerics.INumberBase.IsComplexNumber(System.Numerics.Decimal64 value) { throw null; } static bool System.Numerics.INumberBase.IsImaginaryNumber(System.Numerics.Decimal64 value) { throw null; } @@ -11936,6 +11989,7 @@ namespace System.Numerics public string ToString(System.IFormatProvider? provider) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; } public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal64 Truncate(System.Numerics.Decimal64 x) { throw null; } public bool TryFormat(System.Span utf8Destination, out int bytesWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } public static bool TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal64 result) { 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 f86fbcb5ae524f..0f20a8b5eb1960 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 @@ -1,6 +1,7 @@ // 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.Binary; using System.Collections.Generic; using System.Globalization; using System.Numerics; @@ -1548,6 +1549,16 @@ public static void op_Arithmetic_IntelReferenceVectors(string operation, UInt128 Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal128Modulus), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Modulus_IntelReferenceVectors(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 l = Unsafe.BitCast(left); + Decimal128 r = Unsafe.BitCast(right); + + Assert.Equal(expected, Unsafe.BitCast(l % r)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal128Comparison), MemberType = typeof(DecimalIeee754IntelTestData))] public static void op_Comparison_IntelReferenceVectors(string operation, UInt128 left, UInt128 right, bool expected) @@ -1585,6 +1596,26 @@ public static void UnaryOperation_IntelReferenceVectors(string operation, UInt12 Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal128RoundIntegral), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void RoundIntegral_IntelReferenceVectors(string operation, UInt128 value, UInt128 expected) + { + Decimal128 v = Unsafe.BitCast(value); + + Decimal128 result = operation switch + { + "round_integral_exact" => Decimal128.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_even" => Decimal128.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_away" => Decimal128.Round(v, 0, MidpointRounding.AwayFromZero), + "round_integral_negative" => Decimal128.Floor(v), + "round_integral_positive" => Decimal128.Ceiling(v), + "round_integral_zero" => Decimal128.Truncate(v), + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal128BinaryValue), MemberType = typeof(DecimalIeee754IntelTestData))] public static void BinaryValueOperation_IntelReferenceVectors(string operation, UInt128 left, UInt128 right, UInt128 expected) @@ -2015,6 +2046,8 @@ public static IEnumerable MaxMagnitude_TestData() yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // non-canonical NaN operand is canonicalized + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2040,6 +2073,8 @@ public static IEnumerable MinMagnitude_TestData() yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000005) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // non-canonical NaN operand is canonicalized + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2065,6 +2100,8 @@ public static IEnumerable MaxMagnitudeNumber_TestData() yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000003) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000005) }; // non-canonical NaN dropped in favor of the number + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // both NaN -> first operand canonicalized } [Theory] @@ -2090,6 +2127,8 @@ public static IEnumerable MinMagnitudeNumber_TestData() yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000003) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000005) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000005) }; // non-canonical NaN dropped in favor of the number + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // both NaN -> first operand canonicalized } [Theory] @@ -2144,6 +2183,8 @@ public static IEnumerable Max_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // non-canonical NaN operand is canonicalized + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2179,6 +2220,8 @@ public static IEnumerable Min_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // non-canonical NaN operand is canonicalized + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2214,6 +2257,8 @@ public static IEnumerable MaxNative_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // NaN operand wins and is canonicalized + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // both NaN -> second operand canonicalized } [Theory] @@ -2249,6 +2294,8 @@ public static IEnumerable MinNative_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // NaN operand wins and is canonicalized + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // both NaN -> second operand canonicalized } [Theory] @@ -2284,6 +2331,8 @@ public static IEnumerable MaxNumber_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000005) }; // non-canonical NaN dropped in favor of the number + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // both NaN -> first operand canonicalized } [Theory] @@ -2319,6 +2368,8 @@ public static IEnumerable MinNumber_TestData() yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; yield return new object[] { new UInt128(0x7C00000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000005) }; // non-canonical NaN dropped in favor of the number + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), new UInt128(0x7C00000000000000, 0x0000000000001234) }; // both NaN -> first operand canonicalized } [Theory] @@ -2531,5 +2582,179 @@ public static void CreateToSystemDecimalTest() Assert.Throws(() => decimal.CreateChecked(Decimal128.MaxValue)); Assert.Throws(() => decimal.CreateChecked(Decimal128.NaN)); } + + public static IEnumerable op_Modulus_TestData() + { + yield return new object[] { new UInt128(0x4810000000000000, 0x0000000000000000), new UInt128(0x990E000000000000, 0x00727CEB5CF62962), new UInt128(0x190E000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x02DC000000000000, 0x000000000000C323), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x02DC000000000000, 0x000000000000C323) }; + yield return new object[] { new UInt128(0x0820000000000000, 0x000C7F077915197C), new UInt128(0x557C000000000000, 0x0B1E07FE86637A2F), new UInt128(0x0820000000000000, 0x000C7F077915197C) }; + yield return new object[] { new UInt128(0x19D815CE6B5B34C3, 0x8F50B6B1CC625385), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xC35E00001C26FEE5, 0x1B671598D84E4FDE), new UInt128(0xD6D6000000000000, 0x00000001979A8758), new UInt128(0xC35E00001C26FEE5, 0x1B671598D84E4FDE) }; + yield return new object[] { new UInt128(0x36840000000810A5, 0xB6D5CBDA7C41FE15), new UInt128(0xA602000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x4BE200000000885C, 0x1D87CD3186D0C6CC), new UInt128(0x8180000000000000, 0x0000000000006BD7), new UInt128(0x0180000000000000, 0x00000000000009B8) }; + yield return new object[] { new UInt128(0xA9100000000001C7, 0x5A954BD2316F738B), new UInt128(0x4E34000000000AB0, 0x19F2EB310DEB90C4), new UInt128(0xA9100000000001C7, 0x5A954BD2316F738B) }; + yield return new object[] { new UInt128(0x5B74000000BE82C5, 0xEEE7839C9CF8EDF7), new UInt128(0x4030000000000000, 0x001FFAF9A71D2883), new UInt128(0x4030000000000000, 0x001CD7AC6FE3A534) }; + yield return new object[] { new UInt128(0x8102000A63BCF670, 0xD60A6355E652AD8A), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x901C000000000000, 0x4C27457B78954A33), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xBE8800005BE01275, 0xF6418621117238DD), new UInt128(0xD932000000000000, 0x00000000000D8679), new UInt128(0xBE8800005BE01275, 0xF6418621117238DD) }; + yield return new object[] { new UInt128(0xB250000000000000, 0x0000000000000000), new UInt128(0x2504000000000000, 0x000000000002E306), new UInt128(0xA504000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x1344000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xDE2B69A7FDF37D69, 0x0C83953B47DD2851), new UInt128(0x0DB804DBC574207F, 0x31843184B6F0A80A), new UInt128(0x8DB8016B5216022A, 0x9B020AB9F393AFFC) }; + yield return new object[] { new UInt128(0xC38400001491446B, 0x673D6453971DBCAB), new UInt128(0x1A4A000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x2720000000000000, 0x0000097407D8946B), new UInt128(0xB5F4000000000000, 0x00000000000AF371), new UInt128(0x2720000000000000, 0x0000097407D8946B) }; + yield return new object[] { new UInt128(0x9BF6000000000000, 0x1A30D732F747DD0E), new UInt128(0xA3B8000ABA01A581, 0x4406F1E2E09B39B9), new UInt128(0x9BF6000000000000, 0x1A30D732F747DD0E) }; + yield return new object[] { new UInt128(0x0092000000000000, 0x00000000399224C3), new UInt128(0x5AEC000000000000, 0x0000096C2B8F87F0), new UInt128(0x0092000000000000, 0x00000000399224C3) }; + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x8958000000000000, 0x000000000FC25A7C), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x57B2000000000000, 0x000F5DCED6652344), new UInt128(0xB5A2000000001139, 0x85837D2F6B305C5F), new UInt128(0x35A20000000009F6, 0x20430D5F4C647EA6) }; + yield return new object[] { new UInt128(0xAD78000000000000, 0x0000000000000000), new UInt128(0x346E000000000016, 0xF78007A0158F736C), new UInt128(0xAD78000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xA7B4000000000000, 0x0000000000000001), new UInt128(0x26B4000000000000, 0x03345E83946E53FD), new UInt128(0xA6B4000000000000, 0x007817AA3A3AB10A) }; + yield return new object[] { new UInt128(0x0DE2000000000003, 0xF7F0A35B2122BACC), new UInt128(0x40B6000000000000, 0x00000001C0645AF2), new UInt128(0x0DE2000000000003, 0xF7F0A35B2122BACC) }; + yield return new object[] { new UInt128(0xA984000591CB546F, 0xCDCB05B27845D2D7), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xA984000591CB546F, 0xCDCB05B27845D2D7) }; + yield return new object[] { new UInt128(0x99D4000000000000, 0x0000000000000000), new UInt128(0x01CA000000000000, 0x00003FCDF3F54CA3), new UInt128(0x81CA000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x5ECE0059773868D9, 0xD79D80205EF487AD), new UInt128(0x575C000000000000, 0x000000000000250A), new UInt128(0x575C000000000000, 0x000000000000012C) }; + yield return new object[] { new UInt128(0xAA26000000000000, 0x0000134F75405BA4), new UInt128(0x977C000000000000, 0x000003CC1F1EFDB2), new UInt128(0x977C000000000000, 0x000001BF28D406A8) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x97D0000000000000, 0x0000000000000347), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x546C000000000000, 0x00000000000000C7), new UInt128(0xD59C000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x52DC000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x4BAC0000000015DC, 0xF9962B8B9EA1F26D), new UInt128(0x40D2000000000000, 0x0000000000000007), new UInt128(0x40D2000000000000, 0x0000000000000002) }; + yield return new object[] { new UInt128(0xBABA045882CF1D86, 0x95BA36F8663F9C4C), new UInt128(0x8220000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x386E00000000003C, 0x845CF4E0C470005B), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x5DDD4F2CD7014F7C, 0xF0B4AEB4AEDB5010), new UInt128(0x8AA20000000001C6, 0x4D4C69C05E1AEEC9), new UInt128(0x0AA200000000012C, 0xBDCF5BE2645FEAEF) }; + yield return new object[] { new UInt128(0x9CF600000023177C, 0x325CB9124638EB43), new UInt128(0xC77E000000000000, 0x0000000000000009), new UInt128(0x9CF600000023177C, 0x325CB9124638EB43) }; + yield return new object[] { new UInt128(0x3A72000000000000, 0x0000000000000000), new UInt128(0x3752000000000000, 0x000000B07C9C82E5), new UInt128(0x3752000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xB3E800008DD69B34, 0x8898B05BA9A564C1), new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xBA20000000000000, 0x0000000000001C83), new UInt128(0x3D1C000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; + } + + [Theory] + [MemberData(nameof(op_Modulus_TestData))] + public static void op_Modulus(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(left) % Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable RoundToDigits_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), 0, MidpointRounding.ToEven, new UInt128(0xFC00000000000000, 0x0000000000000000) }; // canonical NaN passes through + yield return new object[] { new UInt128(0x7E00000000000000, 0x0000000000001234), 0, MidpointRounding.ToEven, new UInt128(0x7C00000000000000, 0x0000000000001234) }; // signaling NaN -> quiet NaN (payload preserved) + yield return new object[] { new UInt128(0x7C003FFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF), 3, MidpointRounding.ToZero, new UInt128(0x7C00000000000000, 0x0000000000000000) }; // out-of-range NaN payload cleared + yield return new object[] { new UInt128(0xFE00000000000000, 0x0000000000000000), 2, MidpointRounding.ToNegativeInfinity, new UInt128(0xFC00000000000000, 0x0000000000000000) }; // negative signaling NaN -> quiet NaN (sign preserved) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), 3, MidpointRounding.AwayFromZero, new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf passes through + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), 2, MidpointRounding.ToPositiveInfinity, new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf passes through + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000019), 0, MidpointRounding.ToEven, new UInt128(0x3040000000000000, 0x0000000000000002) }; // 2.5 -> 2 (ToEven) + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000019), 0, MidpointRounding.AwayFromZero, new UInt128(0x3040000000000000, 0x0000000000000003) }; // 2.5 -> 3 (AwayFromZero) + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000023), 0, MidpointRounding.ToEven, new UInt128(0x3040000000000000, 0x0000000000000004) }; // 3.5 -> 4 (ToEven) + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000005), 0, MidpointRounding.ToEven, new UInt128(0x3040000000000000, 0x0000000000000000) }; // 0.5 -> 0 (ToEven) + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000005), 0, MidpointRounding.ToPositiveInfinity, new UInt128(0x3040000000000000, 0x0000000000000001) }; // 0.5 -> 1 (ToPositiveInfinity) + yield return new object[] { new UInt128(0xB03E000000000000, 0x0000000000000005), 0, MidpointRounding.ToNegativeInfinity, new UInt128(0xB040000000000000, 0x0000000000000001) }; // -0.5 -> -1 (ToNegativeInfinity) + yield return new object[] { new UInt128(0xB03C000000000000, 0x0000000000000019), 0, MidpointRounding.ToPositiveInfinity, new UInt128(0xB040000000000000, 0x0000000000000000) }; // -0.25 -> -0 (ToPositiveInfinity) + yield return new object[] { new UInt128(0xB03C000000000000, 0x0000000000000019), 0, MidpointRounding.ToNegativeInfinity, new UInt128(0xB040000000000000, 0x0000000000000001) }; // -0.25 -> -1 (ToNegativeInfinity) + yield return new object[] { new UInt128(0x303C000000000000, 0x0000000000000019), 0, MidpointRounding.ToZero, new UInt128(0x3040000000000000, 0x0000000000000000) }; // 0.25 -> 0 (ToZero) + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000005), 5, MidpointRounding.ToEven, new UInt128(0x303E000000000000, 0x0000000000000005) }; // already finer than target, no-op + yield return new object[] { new UInt128(0xB03E000000000000, 0x0000000000000000), 0, MidpointRounding.ToNegativeInfinity, new UInt128(0xB040000000000000, 0x0000000000000000) }; // -0 stays -0 (ToNegativeInfinity) + yield return new object[] { new UInt128(0x303A000000000000, 0x000000000001E240), 2, MidpointRounding.ToEven, new UInt128(0x303C000000000000, 0x000000000000303A) }; // 123.456 -> 123.46 (ToEven) + yield return new object[] { new UInt128(0xACA200001A64F0B2, 0x47116E8F4AA7655A), 1, MidpointRounding.AwayFromZero, new UInt128(0xB03E000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xBBD6000000000000, 0x0000000000000000), 34, MidpointRounding.AwayFromZero, new UInt128(0xBBD6000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x5CB4020BD4E73D0E, 0x6B8B8C501BF89C14), 19, MidpointRounding.ToEven, new UInt128(0x5CB4020BD4E73D0E, 0x6B8B8C501BF89C14) }; + yield return new object[] { new UInt128(0x4C48000000000000, 0x00000001DD06A8E5), 32, MidpointRounding.ToZero, new UInt128(0x4C48000000000000, 0x00000001DD06A8E5) }; + yield return new object[] { new UInt128(0x5008000000000000, 0x00000000008BDB96), 22, MidpointRounding.ToZero, new UInt128(0x5008000000000000, 0x00000000008BDB96) }; + yield return new object[] { new UInt128(0x44A600000004A2E4, 0x946727A4C20797B4), 35, MidpointRounding.AwayFromZero, new UInt128(0x44A600000004A2E4, 0x946727A4C20797B4) }; + yield return new object[] { new UInt128(0xA5940002963B08E8, 0x1CFEC92070EC50BB), 15, MidpointRounding.AwayFromZero, new UInt128(0xB022000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x9EE8000000000000, 0x0000000000000000), 34, MidpointRounding.ToPositiveInfinity, new UInt128(0xAFFC000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x81E4000000000000, 0x000000000000F435), 9, MidpointRounding.ToNegativeInfinity, new UInt128(0xB02E000000000000, 0x0000000000000001) }; + yield return new object[] { new UInt128(0x4878DE405BD7331D, 0x0541CCBC7509BA76), 8, MidpointRounding.ToNegativeInfinity, new UInt128(0x4878DE405BD7331D, 0x0541CCBC7509BA76) }; + yield return new object[] { new UInt128(0x1A40000000000000, 0x0000000000208C95), 30, MidpointRounding.ToZero, new UInt128(0x3004000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), 32, MidpointRounding.ToZero, new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x83BC000000000000, 0x246A51A7EDEE468A), 16, MidpointRounding.AwayFromZero, new UInt128(0xB020000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), 36, MidpointRounding.ToZero, new UInt128(0x7800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x2A6C000000000018, 0x524FE2F25ACC517F), 13, MidpointRounding.ToEven, new UInt128(0x3026000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x4E8E295B67DFC0C6, 0xE4BC3F5859D95A1C), 18, MidpointRounding.AwayFromZero, new UInt128(0x4E8E295B67DFC0C6, 0xE4BC3F5859D95A1C) }; + yield return new object[] { new UInt128(0xC234000000000000, 0x00000000003638E6), 7, MidpointRounding.ToZero, new UInt128(0xC234000000000000, 0x00000000003638E6) }; + yield return new object[] { new UInt128(0x2448000000000003, 0xC16B45BF142025EA), 24, MidpointRounding.ToPositiveInfinity, new UInt128(0x3010000000000000, 0x0000000000000001) }; + yield return new object[] { new UInt128(0x4F96000000000000, 0x000000000000027D), 6, MidpointRounding.ToZero, new UInt128(0x4F96000000000000, 0x000000000000027D) }; + yield return new object[] { new UInt128(0xCC54000000000000, 0x00000000005DFBBC), 16, MidpointRounding.ToEven, new UInt128(0xCC54000000000000, 0x00000000005DFBBC) }; + yield return new object[] { new UInt128(0x8030000000000000, 0x000000000000002F), 13, MidpointRounding.ToEven, new UInt128(0xB026000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), 20, MidpointRounding.ToZero, new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x1F64000000000000, 0x0000000000000000), 14, MidpointRounding.ToZero, new UInt128(0x3024000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x2D9C000000000000, 0x000000000000AF5A), 11, MidpointRounding.ToEven, new UInt128(0x302A000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xAF9C000000000000, 0x0000000000000032), 24, MidpointRounding.ToPositiveInfinity, new UInt128(0xB010000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x5F9E0000000003FC, 0x7B93612B2A7B1A77), 20, MidpointRounding.ToZero, new UInt128(0x5F9E0000000003FC, 0x7B93612B2A7B1A77) }; + yield return new object[] { new UInt128(0x5ABE000000000000, 0x0000090FF790C39F), 36, MidpointRounding.ToEven, new UInt128(0x5ABE000000000000, 0x0000090FF790C39F) }; + yield return new object[] { new UInt128(0x1F34000000000000, 0x0000000000704EFF), 34, MidpointRounding.ToZero, new UInt128(0x2FFC000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xB088000000000000, 0x00000007B16C6B74), 27, MidpointRounding.ToZero, new UInt128(0xB088000000000000, 0x00000007B16C6B74) }; + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), 9, MidpointRounding.ToZero, new UInt128(0xF800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x8848000001DF35DC, 0xB97C8D8D51686009), 13, MidpointRounding.ToNegativeInfinity, new UInt128(0xB026000000000000, 0x0000000000000001) }; + yield return new object[] { new UInt128(0x2412000000017DA7, 0xA70161F30FBAD560), 13, MidpointRounding.AwayFromZero, new UInt128(0x3026000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x43A4007DC21E3069, 0xD3DC36E844F4E8E4), 16, MidpointRounding.ToZero, new UInt128(0x43A4007DC21E3069, 0xD3DC36E844F4E8E4) }; + yield return new object[] { new UInt128(0x38E4000000027ED7, 0x90B063487ECA3640), 20, MidpointRounding.ToEven, new UInt128(0x38E4000000027ED7, 0x90B063487ECA3640) }; + yield return new object[] { new UInt128(0xC326000000000000, 0x000000021EBC0119), 30, MidpointRounding.AwayFromZero, new UInt128(0xC326000000000000, 0x000000021EBC0119) }; + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), 12, MidpointRounding.ToNegativeInfinity, new UInt128(0xFC00000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x4A40000000000000, 0x000000000000038D), 11, MidpointRounding.ToNegativeInfinity, new UInt128(0x4A40000000000000, 0x000000000000038D) }; + yield return new object[] { new UInt128(0x3382D503E84E1B51, 0x5301BCC58952CAB2), 29, MidpointRounding.ToEven, new UInt128(0x3382D503E84E1B51, 0x5301BCC58952CAB2) }; + yield return new object[] { new UInt128(0x9FBE000000000000, 0x0000000000000363), 24, MidpointRounding.ToEven, new UInt128(0xB010000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0x374A000000000000, 0x000000A466DBFDD5), 21, MidpointRounding.ToZero, new UInt128(0x374A000000000000, 0x000000A466DBFDD5) }; + } + + [Theory] + [MemberData(nameof(RoundToDigits_TestData))] + public static void RoundToDigits(UInt128 value, int digits, MidpointRounding mode, UInt128 expected) + { + Decimal128 result = Decimal128.Round(Unsafe.BitCast(value), digits, mode); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Fact] + public static void RoundConvenienceOverloads() + { + Decimal128 x = Unsafe.BitCast(new UInt128(0x303E000000000000, 0x0000000000000019)); // 2.5 + + Assert.Equal(Decimal128.Round(x, 0, MidpointRounding.ToPositiveInfinity), Decimal128.Ceiling(x)); + Assert.Equal(Decimal128.Round(x, 0, MidpointRounding.ToNegativeInfinity), Decimal128.Floor(x)); + Assert.Equal(Decimal128.Round(x, 0, MidpointRounding.ToZero), Decimal128.Truncate(x)); + Assert.Equal(Decimal128.Round(x, 0, MidpointRounding.ToEven), Decimal128.Round(x)); + Assert.Equal(Decimal128.Round(x, 0, MidpointRounding.AwayFromZero), Decimal128.Round(x, MidpointRounding.AwayFromZero)); + Assert.Equal(Decimal128.Round(x, 2, MidpointRounding.ToEven), Decimal128.Round(x, 2)); + } + + [Fact] + public static void IFloatingPoint_ExponentAndSignificand() + { + IFloatingPoint value = Unsafe.BitCast(new UInt128(0x303C000000000000, 0x0000000000003039)); // 123.45 + + Assert.Equal(sizeof(int), value.GetExponentByteCount()); + Assert.Equal(Unsafe.SizeOf(), value.GetSignificandByteCount()); + + Span exponent = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentLittleEndian(exponent, out int exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32LittleEndian(exponent)); + + Span significand = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandLittleEndian(significand, out int significandWritten)); + Assert.Equal(Unsafe.SizeOf(), significandWritten); + Assert.Equal((UInt128)12345, BinaryPrimitives.ReadUInt128LittleEndian(significand)); + + Assert.Equal(2, value.GetExponentShortestBitLength()); + Assert.Equal(113, value.GetSignificandBitLength()); + + Span exponentBigEndian = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentBigEndian(exponentBigEndian, out exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32BigEndian(exponentBigEndian)); + + Span significandBigEndian = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandBigEndian(significandBigEndian, out significandWritten)); + Assert.Equal(Unsafe.SizeOf(), significandWritten); + Assert.Equal((UInt128)12345, BinaryPrimitives.ReadUInt128BigEndian(significandBigEndian)); + + // A non-negative exponent exercises the other GetExponentShortestBitLength branch. + IFloatingPoint integer = Unsafe.BitCast(new UInt128(0x3040000000000000, 0x0000000000003039)); // 12345 + Assert.Equal(0, integer.GetExponentShortestBitLength()); + + Assert.Equal(123, Decimal128.ConvertToInteger(Unsafe.BitCast(new UInt128(0x303C000000000000, 0x0000000000003039)))); + } + } } 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 28e8231006acd7..0598858a3ddf17 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 @@ -1,6 +1,7 @@ // 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.Binary; using System.Collections.Generic; using System.Globalization; using System.Numerics; @@ -1548,6 +1549,16 @@ public static void op_Arithmetic_IntelReferenceVectors(string operation, uint le Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal32Modulus), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Modulus_IntelReferenceVectors(uint left, uint right, uint expected) + { + Decimal32 l = Unsafe.BitCast(left); + Decimal32 r = Unsafe.BitCast(right); + + Assert.Equal(expected, Unsafe.BitCast(l % r)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal32Comparison), MemberType = typeof(DecimalIeee754IntelTestData))] public static void op_Comparison_IntelReferenceVectors(string operation, uint left, uint right, bool expected) @@ -1585,6 +1596,26 @@ public static void UnaryOperation_IntelReferenceVectors(string operation, uint v Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal32RoundIntegral), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void RoundIntegral_IntelReferenceVectors(string operation, uint value, uint expected) + { + Decimal32 v = Unsafe.BitCast(value); + + Decimal32 result = operation switch + { + "round_integral_exact" => Decimal32.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_even" => Decimal32.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_away" => Decimal32.Round(v, 0, MidpointRounding.AwayFromZero), + "round_integral_negative" => Decimal32.Floor(v), + "round_integral_positive" => Decimal32.Ceiling(v), + "round_integral_zero" => Decimal32.Truncate(v), + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal32BinaryValue), MemberType = typeof(DecimalIeee754IntelTestData))] public static void BinaryValueOperation_IntelReferenceVectors(string operation, uint left, uint right, uint expected) @@ -2014,6 +2045,8 @@ public static IEnumerable MaxMagnitude_TestData() yield return new object[] { 0x32800003U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0x7C000000U }; yield return new object[] { 0xB2800005U, 0x78000000U, 0x78000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x7C001234U }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x32800005U, 0x7C0FFFFFU, 0x7C000000U }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2039,6 +2072,8 @@ public static IEnumerable MinMagnitude_TestData() yield return new object[] { 0x32800003U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0x7C000000U }; yield return new object[] { 0xB2800005U, 0x78000000U, 0xB2800005U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x7C001234U }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x32800005U, 0x7C0FFFFFU, 0x7C000000U }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2064,6 +2099,8 @@ public static IEnumerable MaxMagnitudeNumber_TestData() yield return new object[] { 0x32800003U, 0x7C000000U, 0x32800003U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; yield return new object[] { 0xB2800005U, 0x78000000U, 0x78000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x32800005U }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C001234U }; // both NaN -> first operand canonicalized } [Theory] @@ -2089,6 +2126,8 @@ public static IEnumerable MinMagnitudeNumber_TestData() yield return new object[] { 0x32800003U, 0x7C000000U, 0x32800003U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; yield return new object[] { 0xB2800005U, 0x78000000U, 0xB2800005U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x32800005U }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C001234U }; // both NaN -> first operand canonicalized } [Theory] @@ -2143,6 +2182,8 @@ public static IEnumerable Max_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0x78000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0x7C000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x7C001234U }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x32800005U, 0x7C0FFFFFU, 0x7C000000U }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2178,6 +2219,8 @@ public static IEnumerable Min_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0xF8000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0x7C000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x7C001234U }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x32800005U, 0x7C0FFFFFU, 0x7C000000U }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2213,6 +2256,8 @@ public static IEnumerable MaxNative_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0x78000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; + yield return new object[] { 0x32800005U, 0x7E001234U, 0x7C001234U }; // NaN operand wins and is canonicalized + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C000000U }; // both NaN -> second operand canonicalized } [Theory] @@ -2248,6 +2293,8 @@ public static IEnumerable MinNative_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0xF8000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x7C000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; + yield return new object[] { 0x32800005U, 0x7E001234U, 0x7C001234U }; // NaN operand wins and is canonicalized + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C000000U }; // both NaN -> second operand canonicalized } [Theory] @@ -2283,6 +2330,8 @@ public static IEnumerable MaxNumber_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0x78000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x78000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x32800005U }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C001234U }; // both NaN -> first operand canonicalized } [Theory] @@ -2318,6 +2367,8 @@ public static IEnumerable MinNumber_TestData() yield return new object[] { 0xF8000000U, 0x78000000U, 0xF8000000U }; yield return new object[] { 0x78000000U, 0x7C000000U, 0x78000000U }; yield return new object[] { 0x7C000000U, 0xF8000000U, 0xF8000000U }; + yield return new object[] { 0x7E001234U, 0x32800005U, 0x32800005U }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E001234U, 0x7C0FFFFFU, 0x7C001234U }; // both NaN -> first operand canonicalized } [Theory] @@ -2531,5 +2582,179 @@ public static void CreateToSystemDecimalTest() Assert.Throws(() => decimal.CreateChecked(Decimal32.MaxValue)); Assert.Throws(() => decimal.CreateChecked(Decimal32.NaN)); } + + public static IEnumerable op_Modulus_TestData() + { + yield return new object[] { 0x30800000U, 0x3B802494U, 0x30800000U }; + yield return new object[] { 0x03800001U, 0xD9816ABDU, 0x03800001U }; + yield return new object[] { 0x220005D4U, 0x9C814F1CU, 0x1C80D12CU }; + yield return new object[] { 0xBE8024DDU, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0x0719EB11U, 0xF11248EDU, 0x0719EB11U }; + yield return new object[] { 0x588E93BAU, 0x09800000U, 0x7C000000U }; + yield return new object[] { 0x3F8E737BU, 0x59800005U, 0x3F8E737BU }; + yield return new object[] { 0xBD0CC3EAU, 0xD600096BU, 0xBD0CC3EAU }; + yield return new object[] { 0xD5811B28U, 0x6245C247U, 0x895CDC6DU }; + yield return new object[] { 0x4B000008U, 0xF8000000U, 0x4B000008U }; + yield return new object[] { 0xC0800011U, 0xD7000000U, 0x7C000000U }; + yield return new object[] { 0xCD824997U, 0x0E801B4AU, 0x8E800298U }; + yield return new object[] { 0x58800000U, 0x2B001201U, 0x2B000000U }; + yield return new object[] { 0x44009111U, 0x3D800000U, 0x7C000000U }; + yield return new object[] { 0x11000052U, 0x4E936B7DU, 0x11000052U }; + yield return new object[] { 0xF8000000U, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0xDF815AC4U, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0x070008A0U, 0xC3064EB3U, 0x070008A0U }; + yield return new object[] { 0xDD000004U, 0xDC000009U, 0xDC000004U }; + yield return new object[] { 0xDA08333FU, 0xCA800056U, 0xCA800036U }; + yield return new object[] { 0xAA001D5CU, 0x86827828U, 0x86816690U }; + yield return new object[] { 0x9F85DA07U, 0x389FE11CU, 0x9F85DA07U }; + yield return new object[] { 0xD70013AAU, 0xC5844302U, 0xC581626CU }; + yield return new object[] { 0xAA80890CU, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0x96800218U, 0x3881296CU, 0x96800218U }; + yield return new object[] { 0x4F000000U, 0x19000001U, 0x19000000U }; + yield return new object[] { 0x9E800008U, 0x800D7106U, 0x8008A836U }; + yield return new object[] { 0xF8000000U, 0xCC000000U, 0x7C000000U }; + yield return new object[] { 0x120DBC9EU, 0xD60049FBU, 0x120DBC9EU }; + yield return new object[] { 0x92000C66U, 0xF8000000U, 0x92000C66U }; + yield return new object[] { 0x41CFB625U, 0xF8000000U, 0x41CFB625U }; + yield return new object[] { 0x912AE74FU, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0xA10018DBU, 0xA380107BU, 0xA10018DBU }; + yield return new object[] { 0x78000000U, 0x8D0137A3U, 0x7C000000U }; + yield return new object[] { 0xFC000000U, 0xE8E1DBD4U, 0xFC000000U }; + yield return new object[] { 0x42000000U, 0xB2800000U, 0x7C000000U }; + yield return new object[] { 0xAE800000U, 0x53800007U, 0xAE800000U }; + yield return new object[] { 0xB1800002U, 0xFC000000U, 0xFC000000U }; + yield return new object[] { 0x98000D0EU, 0xF8000000U, 0x98000D0EU }; + yield return new object[] { 0x84C31037U, 0x97000D9FU, 0x84C31037U }; + } + + [Theory] + [MemberData(nameof(op_Modulus_TestData))] + public static void op_Modulus(uint left, uint right, uint expected) + { + Decimal32 result = Unsafe.BitCast(left) % Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable RoundToDigits_TestData() + { + yield return new object[] { 0xFC000000U, 0, MidpointRounding.ToEven, 0xFC000000U }; // canonical NaN passes through + yield return new object[] { 0x7E001234U, 0, MidpointRounding.ToEven, 0x7C001234U }; // signaling NaN -> quiet NaN (payload preserved) + yield return new object[] { 0x7C0FFFFFU, 3, MidpointRounding.ToZero, 0x7C000000U }; // out-of-range NaN payload cleared + yield return new object[] { 0xFE000000U, 2, MidpointRounding.ToNegativeInfinity, 0xFC000000U }; // negative signaling NaN -> quiet NaN (sign preserved) + yield return new object[] { 0x78000000U, 3, MidpointRounding.AwayFromZero, 0x78000000U }; // +Inf passes through + yield return new object[] { 0xF8000000U, 2, MidpointRounding.ToPositiveInfinity, 0xF8000000U }; // -Inf passes through + yield return new object[] { 0x32000019U, 0, MidpointRounding.ToEven, 0x32800002U }; // 2.5 -> 2 (ToEven) + yield return new object[] { 0x32000019U, 0, MidpointRounding.AwayFromZero, 0x32800003U }; // 2.5 -> 3 (AwayFromZero) + yield return new object[] { 0x32000023U, 0, MidpointRounding.ToEven, 0x32800004U }; // 3.5 -> 4 (ToEven) + yield return new object[] { 0x32000005U, 0, MidpointRounding.ToEven, 0x32800000U }; // 0.5 -> 0 (ToEven) + yield return new object[] { 0x32000005U, 0, MidpointRounding.ToPositiveInfinity, 0x32800001U }; // 0.5 -> 1 (ToPositiveInfinity) + yield return new object[] { 0xB2000005U, 0, MidpointRounding.ToNegativeInfinity, 0xB2800001U }; // -0.5 -> -1 (ToNegativeInfinity) + yield return new object[] { 0xB1800019U, 0, MidpointRounding.ToPositiveInfinity, 0xB2800000U }; // -0.25 -> -0 (ToPositiveInfinity) + yield return new object[] { 0xB1800019U, 0, MidpointRounding.ToNegativeInfinity, 0xB2800001U }; // -0.25 -> -1 (ToNegativeInfinity) + yield return new object[] { 0x31800019U, 0, MidpointRounding.ToZero, 0x32800000U }; // 0.25 -> 0 (ToZero) + yield return new object[] { 0x32000005U, 5, MidpointRounding.ToEven, 0x32000005U }; // already finer than target, no-op + yield return new object[] { 0xB2000000U, 0, MidpointRounding.ToNegativeInfinity, 0xB2800000U }; // -0 stays -0 (ToNegativeInfinity) + yield return new object[] { 0x3101E240U, 2, MidpointRounding.ToEven, 0x3180303AU }; // 123.456 -> 123.46 (ToEven) + yield return new object[] { 0x2280020DU, 7, MidpointRounding.ToNegativeInfinity, 0x2F000000U }; + yield return new object[] { 0xD9800006U, 8, MidpointRounding.ToNegativeInfinity, 0xD9800006U }; + yield return new object[] { 0xB4001656U, 3, MidpointRounding.ToZero, 0xB4001656U }; + yield return new object[] { 0x9604DAA5U, 4, MidpointRounding.AwayFromZero, 0xB0800000U }; + yield return new object[] { 0x2880005AU, 8, MidpointRounding.ToZero, 0x2E800000U }; + yield return new object[] { 0xA0000037U, 5, MidpointRounding.ToEven, 0xB0000000U }; + yield return new object[] { 0xA8000000U, 3, MidpointRounding.ToPositiveInfinity, 0xB1000000U }; + yield return new object[] { 0x2A8B1C9BU, 6, MidpointRounding.ToNegativeInfinity, 0x2F800000U }; + yield return new object[] { 0x060006D9U, 2, MidpointRounding.ToEven, 0x31800000U }; + yield return new object[] { 0x9A000289U, 2, MidpointRounding.ToNegativeInfinity, 0xB1800001U }; + yield return new object[] { 0x1D80009CU, 2, MidpointRounding.ToZero, 0x31800000U }; + yield return new object[] { 0x5981B2C3U, 8, MidpointRounding.ToPositiveInfinity, 0x5981B2C3U }; + yield return new object[] { 0x188EEC20U, 2, MidpointRounding.ToPositiveInfinity, 0x31800001U }; + yield return new object[] { 0x5B01FC6AU, 0, MidpointRounding.ToNegativeInfinity, 0x5B01FC6AU }; + yield return new object[] { 0x57801F86U, 1, MidpointRounding.ToEven, 0x57801F86U }; + yield return new object[] { 0x28800000U, 1, MidpointRounding.ToNegativeInfinity, 0x32000000U }; + yield return new object[] { 0x5B01246AU, 7, MidpointRounding.AwayFromZero, 0x5B01246AU }; + yield return new object[] { 0x2A000000U, 3, MidpointRounding.ToNegativeInfinity, 0x31000000U }; + yield return new object[] { 0xAC800000U, 3, MidpointRounding.ToNegativeInfinity, 0xB1000000U }; + yield return new object[] { 0x26B74A57U, 0, MidpointRounding.ToZero, 0x32800000U }; + yield return new object[] { 0xFC000000U, 7, MidpointRounding.ToEven, 0xFC000000U }; + yield return new object[] { 0xDC800000U, 3, MidpointRounding.ToZero, 0xDC800000U }; + yield return new object[] { 0xAE000000U, 7, MidpointRounding.ToEven, 0xAF000000U }; + yield return new object[] { 0x39000000U, 8, MidpointRounding.ToNegativeInfinity, 0x39000000U }; + yield return new object[] { 0x82800000U, 5, MidpointRounding.ToEven, 0xB0000000U }; + yield return new object[] { 0x96800042U, 3, MidpointRounding.ToPositiveInfinity, 0xB1000000U }; + yield return new object[] { 0x4B8009DAU, 6, MidpointRounding.ToPositiveInfinity, 0x4B8009DAU }; + yield return new object[] { 0xB3800014U, 2, MidpointRounding.ToEven, 0xB3800014U }; + yield return new object[] { 0xA90015F5U, 4, MidpointRounding.ToNegativeInfinity, 0xB0800001U }; + yield return new object[] { 0x78000000U, 3, MidpointRounding.ToZero, 0x78000000U }; + yield return new object[] { 0xD4801DA3U, 5, MidpointRounding.ToEven, 0xD4801DA3U }; + yield return new object[] { 0x08016897U, 1, MidpointRounding.ToPositiveInfinity, 0x32000001U }; + yield return new object[] { 0xB5000000U, 0, MidpointRounding.ToZero, 0xB5000000U }; + yield return new object[] { 0xFC000000U, 2, MidpointRounding.ToNegativeInfinity, 0xFC000000U }; + yield return new object[] { 0xD02E0F8DU, 6, MidpointRounding.ToZero, 0xD02E0F8DU }; + yield return new object[] { 0x9980236DU, 6, MidpointRounding.AwayFromZero, 0xAF800000U }; + yield return new object[] { 0x3980E252U, 7, MidpointRounding.ToEven, 0x3980E252U }; + yield return new object[] { 0x9B8B73CFU, 6, MidpointRounding.ToEven, 0xAF800000U }; + yield return new object[] { 0x2E00034EU, 0, MidpointRounding.AwayFromZero, 0x32800000U }; + yield return new object[] { 0xAE8C7F3EU, 9, MidpointRounding.ToPositiveInfinity, 0xAE8C7F3EU }; + } + + [Theory] + [MemberData(nameof(RoundToDigits_TestData))] + public static void RoundToDigits(uint value, int digits, MidpointRounding mode, uint expected) + { + Decimal32 result = Decimal32.Round(Unsafe.BitCast(value), digits, mode); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Fact] + public static void RoundConvenienceOverloads() + { + Decimal32 x = Unsafe.BitCast(0x32000019U); // 2.5 + + Assert.Equal(Decimal32.Round(x, 0, MidpointRounding.ToPositiveInfinity), Decimal32.Ceiling(x)); + Assert.Equal(Decimal32.Round(x, 0, MidpointRounding.ToNegativeInfinity), Decimal32.Floor(x)); + Assert.Equal(Decimal32.Round(x, 0, MidpointRounding.ToZero), Decimal32.Truncate(x)); + Assert.Equal(Decimal32.Round(x, 0, MidpointRounding.ToEven), Decimal32.Round(x)); + Assert.Equal(Decimal32.Round(x, 0, MidpointRounding.AwayFromZero), Decimal32.Round(x, MidpointRounding.AwayFromZero)); + Assert.Equal(Decimal32.Round(x, 2, MidpointRounding.ToEven), Decimal32.Round(x, 2)); + } + + [Fact] + public static void IFloatingPoint_ExponentAndSignificand() + { + IFloatingPoint value = Unsafe.BitCast(0x31803039U); // 123.45 + + Assert.Equal(sizeof(int), value.GetExponentByteCount()); + Assert.Equal(sizeof(uint), value.GetSignificandByteCount()); + + Span exponent = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentLittleEndian(exponent, out int exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32LittleEndian(exponent)); + + Span significand = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandLittleEndian(significand, out int significandWritten)); + Assert.Equal(sizeof(uint), significandWritten); + Assert.Equal(12345u, BinaryPrimitives.ReadUInt32LittleEndian(significand)); + + Assert.Equal(2, value.GetExponentShortestBitLength()); + Assert.Equal(24, value.GetSignificandBitLength()); + + Span exponentBigEndian = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentBigEndian(exponentBigEndian, out exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32BigEndian(exponentBigEndian)); + + Span significandBigEndian = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandBigEndian(significandBigEndian, out significandWritten)); + Assert.Equal(sizeof(uint), significandWritten); + Assert.Equal(12345u, BinaryPrimitives.ReadUInt32BigEndian(significandBigEndian)); + + // A non-negative exponent exercises the other GetExponentShortestBitLength branch. + IFloatingPoint integer = Unsafe.BitCast(0x32803039U); // 12345 + Assert.Equal(0, integer.GetExponentShortestBitLength()); + + Assert.Equal(123, Decimal32.ConvertToInteger(Unsafe.BitCast(0x31803039U))); + } + } } 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 d7772530c8730a..d24dc9e51dc78d 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 @@ -1,6 +1,7 @@ // 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.Binary; using System.Collections.Generic; using System.Globalization; using System.Numerics; @@ -1554,6 +1555,16 @@ public static void op_Arithmetic_IntelReferenceVectors(string operation, ulong l Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal64Modulus), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Modulus_IntelReferenceVectors(ulong left, ulong right, ulong expected) + { + Decimal64 l = Unsafe.BitCast(left); + Decimal64 r = Unsafe.BitCast(right); + + Assert.Equal(expected, Unsafe.BitCast(l % r)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal64Comparison), MemberType = typeof(DecimalIeee754IntelTestData))] public static void op_Comparison_IntelReferenceVectors(string operation, ulong left, ulong right, bool expected) @@ -1591,6 +1602,26 @@ public static void UnaryOperation_IntelReferenceVectors(string operation, ulong Assert.Equal(expected, Unsafe.BitCast(result)); } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal64RoundIntegral), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void RoundIntegral_IntelReferenceVectors(string operation, ulong value, ulong expected) + { + Decimal64 v = Unsafe.BitCast(value); + + Decimal64 result = operation switch + { + "round_integral_exact" => Decimal64.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_even" => Decimal64.Round(v, 0, MidpointRounding.ToEven), + "round_integral_nearest_away" => Decimal64.Round(v, 0, MidpointRounding.AwayFromZero), + "round_integral_negative" => Decimal64.Floor(v), + "round_integral_positive" => Decimal64.Ceiling(v), + "round_integral_zero" => Decimal64.Truncate(v), + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] [MemberData(nameof(DecimalIeee754IntelTestData.Decimal64BinaryValue), MemberType = typeof(DecimalIeee754IntelTestData))] public static void BinaryValueOperation_IntelReferenceVectors(string operation, ulong left, ulong right, ulong expected) @@ -2018,6 +2049,8 @@ public static IEnumerable MaxMagnitude_TestData() yield return new object[] { 0x31C0000000000003UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0xB1C0000000000005UL, 0x7800000000000000UL, 0x7800000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x7C00000000001234UL }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x31C0000000000005UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2043,6 +2076,8 @@ public static IEnumerable MinMagnitude_TestData() yield return new object[] { 0x31C0000000000003UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0xB1C0000000000005UL, 0x7800000000000000UL, 0xB1C0000000000005UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x7C00000000001234UL }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x31C0000000000005UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2068,6 +2103,8 @@ public static IEnumerable MaxMagnitudeNumber_TestData() yield return new object[] { 0x31C0000000000003UL, 0x7C00000000000000UL, 0x31C0000000000003UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; yield return new object[] { 0xB1C0000000000005UL, 0x7800000000000000UL, 0x7800000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x31C0000000000005UL }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000001234UL }; // both NaN -> first operand canonicalized } [Theory] @@ -2093,6 +2130,8 @@ public static IEnumerable MinMagnitudeNumber_TestData() yield return new object[] { 0x31C0000000000003UL, 0x7C00000000000000UL, 0x31C0000000000003UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; yield return new object[] { 0xB1C0000000000005UL, 0x7800000000000000UL, 0xB1C0000000000005UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x31C0000000000005UL }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000001234UL }; // both NaN -> first operand canonicalized } [Theory] @@ -2147,6 +2186,8 @@ public static IEnumerable Max_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0x7800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x7C00000000001234UL }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x31C0000000000005UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2182,6 +2223,8 @@ public static IEnumerable Min_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0xF800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x7C00000000001234UL }; // non-canonical NaN operand is canonicalized + yield return new object[] { 0x31C0000000000005UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // non-canonical NaN operand is canonicalized } [Theory] @@ -2217,6 +2260,8 @@ public static IEnumerable MaxNative_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0x7800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; + yield return new object[] { 0x31C0000000000005UL, 0x7E00000000001234UL, 0x7C00000000001234UL }; // NaN operand wins and is canonicalized + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // both NaN -> second operand canonicalized } [Theory] @@ -2252,6 +2297,8 @@ public static IEnumerable MinNative_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0xF800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7C00000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; + yield return new object[] { 0x31C0000000000005UL, 0x7E00000000001234UL, 0x7C00000000001234UL }; // NaN operand wins and is canonicalized + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000000000UL }; // both NaN -> second operand canonicalized } [Theory] @@ -2287,6 +2334,8 @@ public static IEnumerable MaxNumber_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0x7800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7800000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x31C0000000000005UL }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000001234UL }; // both NaN -> first operand canonicalized } [Theory] @@ -2322,6 +2371,8 @@ public static IEnumerable MinNumber_TestData() yield return new object[] { 0xF800000000000000UL, 0x7800000000000000UL, 0xF800000000000000UL }; yield return new object[] { 0x7800000000000000UL, 0x7C00000000000000UL, 0x7800000000000000UL }; yield return new object[] { 0x7C00000000000000UL, 0xF800000000000000UL, 0xF800000000000000UL }; + yield return new object[] { 0x7E00000000001234UL, 0x31C0000000000005UL, 0x31C0000000000005UL }; // non-canonical NaN dropped in favor of the number + yield return new object[] { 0x7E00000000001234UL, 0x7C03FFFFFFFFFFFFUL, 0x7C00000000001234UL }; // both NaN -> first operand canonicalized } [Theory] @@ -2537,5 +2588,179 @@ public static void CreateToSystemDecimalTest() Assert.Throws(() => decimal.CreateChecked(Decimal64.MaxValue)); Assert.Throws(() => decimal.CreateChecked(Decimal64.NaN)); } + + public static IEnumerable op_Modulus_TestData() + { + yield return new object[] { 0x41A00000002DEBDBUL, 0x17C0000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0xCA2000000000004FUL, 0x7800000000000000UL, 0xCA2000000000004FUL }; + yield return new object[] { 0xB6600000004150C1UL, 0xBCC0000000002448UL, 0xB6600000004150C1UL }; + yield return new object[] { 0x7800000000000000UL, 0xB1E00000000019C3UL, 0x7C00000000000000UL }; + yield return new object[] { 0x4960000000000000UL, 0x0DC0000000000024UL, 0x0DC0000000000000UL }; + yield return new object[] { 0x10EE600FD19029CBUL, 0xA6A000000094E674UL, 0x10EE600FD19029CBUL }; + yield return new object[] { 0x85CF1FFC772F327BUL, 0xBBA00000268EDEFFUL, 0x85CF1FFC772F327BUL }; + yield return new object[] { 0x48A0000000060DE1UL, 0xB5E01EFC5808BE55UL, 0x35E00BCDA6CEDA71UL }; + yield return new object[] { 0x2D80000000000012UL, 0xD6600000007B80C8UL, 0x2D80000000000012UL }; + yield return new object[] { 0xAFE0000000000000UL, 0x36E0000000000019UL, 0xAFE0000000000000UL }; + yield return new object[] { 0x33227424D5C43DDEUL, 0xCDE0029AD1B2EAB5UL, 0x33227424D5C43DDEUL }; + yield return new object[] { 0x81A0000000000000UL, 0xDFFE6402B104AB83UL, 0x81A0000000000000UL }; + yield return new object[] { 0xF800000000000000UL, 0xFC00000000000000UL, 0xFC00000000000000UL }; + yield return new object[] { 0x8A20000000001260UL, 0x18641BBD81D53E7DUL, 0x8A20000000001260UL }; + yield return new object[] { 0x8D000000000D7F91UL, 0xB960000141EB09DAUL, 0x8D000000000D7F91UL }; + yield return new object[] { 0xE6C19C4850147174UL, 0xB3000000000026E1UL, 0xE6C19C4850147174UL }; + yield return new object[] { 0xC2C000000000003AUL, 0x5FE003C84E63DFC8UL, 0xC2C000000000003AUL }; + yield return new object[] { 0x38C02B1AFA429C27UL, 0x5F800000000038C0UL, 0x38C02B1AFA429C27UL }; + yield return new object[] { 0xBA4000E2D684A0C5UL, 0xCFDEA1F334F310E9UL, 0xBA4000E2D684A0C5UL }; + yield return new object[] { 0x2900000000000000UL, 0x222000040FAA1C34UL, 0x2220000000000000UL }; + yield return new object[] { 0x1D400000006FF219UL, 0x52C00739A4B8C5B2UL, 0x1D400000006FF219UL }; + yield return new object[] { 0xA620000000046867UL, 0xD100000000000004UL, 0xA620000000046867UL }; + yield return new object[] { 0xD5A005E5E3DDA2EDUL, 0xFC00000000000000UL, 0xFC00000000000000UL }; + yield return new object[] { 0xA3400014152667BAUL, 0xCCE0000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x4140000000000000UL, 0x42C000000000001CUL, 0x4140000000000000UL }; + yield return new object[] { 0x07C0047B67EC371CUL, 0x2520000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x8EE2BC0886D8B658UL, 0x9E00000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x15A0000000000007UL, 0x80E0000000002291UL, 0x00E0000000000E1AUL }; + yield return new object[] { 0xC7E0000000000E7CUL, 0x54800000030D836EUL, 0xC7E0000000000E7CUL }; + yield return new object[] { 0x4BE031B4C864F3BEUL, 0xC920000000000007UL, 0x4920000000000000UL }; + yield return new object[] { 0x2BA0009305476749UL, 0x1800000000000294UL, 0x180000000000003CUL }; + yield return new object[] { 0xCB00207A9585137BUL, 0x95C0000000000000UL, 0x7C00000000000000UL }; + yield return new object[] { 0x9920000000000B0EUL, 0x1C40000003C50B28UL, 0x9920000000000B0EUL }; + yield return new object[] { 0x91A0000000000002UL, 0x524000000000003EUL, 0x91A0000000000002UL }; + yield return new object[] { 0x0100000000275140UL, 0xB2A008E9FA40DF3FUL, 0x0100000000275140UL }; + yield return new object[] { 0xBBE00002515A612BUL, 0xAFC0000000000351UL, 0xAFC000000000022CUL }; + yield return new object[] { 0x16E0000000000000UL, 0xAA800004CECAD583UL, 0x16E0000000000000UL }; + yield return new object[] { 0xD5400522C65A0499UL, 0x2CC0000000000017UL, 0xACC000000000000DUL }; + yield return new object[] { 0x0680000000000000UL, 0xEDE9769371960A39UL, 0x0680000000000000UL }; + yield return new object[] { 0x4580000000000000UL, 0x45600000000006C2UL, 0x4560000000000000UL }; + } + + [Theory] + [MemberData(nameof(op_Modulus_TestData))] + public static void op_Modulus(ulong left, ulong right, ulong expected) + { + Decimal64 result = Unsafe.BitCast(left) % Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable RoundToDigits_TestData() + { + yield return new object[] { 0xFC00000000000000UL, 0, MidpointRounding.ToEven, 0xFC00000000000000UL }; // canonical NaN passes through + yield return new object[] { 0x7E00000000001234UL, 0, MidpointRounding.ToEven, 0x7C00000000001234UL }; // signaling NaN -> quiet NaN (payload preserved) + yield return new object[] { 0x7C03FFFFFFFFFFFFUL, 3, MidpointRounding.ToZero, 0x7C00000000000000UL }; // out-of-range NaN payload cleared + yield return new object[] { 0xFE00000000000000UL, 2, MidpointRounding.ToNegativeInfinity, 0xFC00000000000000UL }; // negative signaling NaN -> quiet NaN (sign preserved) + yield return new object[] { 0x7800000000000000UL, 3, MidpointRounding.AwayFromZero, 0x7800000000000000UL }; // +Inf passes through + yield return new object[] { 0xF800000000000000UL, 2, MidpointRounding.ToPositiveInfinity, 0xF800000000000000UL }; // -Inf passes through + yield return new object[] { 0x31A0000000000019UL, 0, MidpointRounding.ToEven, 0x31C0000000000002UL }; // 2.5 -> 2 (ToEven) + yield return new object[] { 0x31A0000000000019UL, 0, MidpointRounding.AwayFromZero, 0x31C0000000000003UL }; // 2.5 -> 3 (AwayFromZero) + yield return new object[] { 0x31A0000000000023UL, 0, MidpointRounding.ToEven, 0x31C0000000000004UL }; // 3.5 -> 4 (ToEven) + yield return new object[] { 0x31A0000000000005UL, 0, MidpointRounding.ToEven, 0x31C0000000000000UL }; // 0.5 -> 0 (ToEven) + yield return new object[] { 0x31A0000000000005UL, 0, MidpointRounding.ToPositiveInfinity, 0x31C0000000000001UL }; // 0.5 -> 1 (ToPositiveInfinity) + yield return new object[] { 0xB1A0000000000005UL, 0, MidpointRounding.ToNegativeInfinity, 0xB1C0000000000001UL }; // -0.5 -> -1 (ToNegativeInfinity) + yield return new object[] { 0xB180000000000019UL, 0, MidpointRounding.ToPositiveInfinity, 0xB1C0000000000000UL }; // -0.25 -> -0 (ToPositiveInfinity) + yield return new object[] { 0xB180000000000019UL, 0, MidpointRounding.ToNegativeInfinity, 0xB1C0000000000001UL }; // -0.25 -> -1 (ToNegativeInfinity) + yield return new object[] { 0x3180000000000019UL, 0, MidpointRounding.ToZero, 0x31C0000000000000UL }; // 0.25 -> 0 (ToZero) + yield return new object[] { 0x31A0000000000005UL, 5, MidpointRounding.ToEven, 0x31A0000000000005UL }; // already finer than target, no-op + yield return new object[] { 0xB1A0000000000000UL, 0, MidpointRounding.ToNegativeInfinity, 0xB1C0000000000000UL }; // -0 stays -0 (ToNegativeInfinity) + yield return new object[] { 0x316000000001E240UL, 2, MidpointRounding.ToEven, 0x318000000000303AUL }; // 123.456 -> 123.46 (ToEven) + yield return new object[] { 0x5164296458D07D2CUL, 4, MidpointRounding.ToZero, 0x5164296458D07D2CUL }; + yield return new object[] { 0x3840000000000000UL, 5, MidpointRounding.ToPositiveInfinity, 0x3840000000000000UL }; + yield return new object[] { 0xFC00000000000000UL, 12, MidpointRounding.ToNegativeInfinity, 0xFC00000000000000UL }; + yield return new object[] { 0xC820008FE40A9785UL, 11, MidpointRounding.ToZero, 0xC820008FE40A9785UL }; + yield return new object[] { 0x9640000000000000UL, 0, MidpointRounding.ToZero, 0xB1C0000000000000UL }; + yield return new object[] { 0xF800000000000000UL, 2, MidpointRounding.AwayFromZero, 0xF800000000000000UL }; + yield return new object[] { 0xD2200012C6ADE789UL, 15, MidpointRounding.AwayFromZero, 0xD2200012C6ADE789UL }; + yield return new object[] { 0xD4C0000000097DE0UL, 16, MidpointRounding.ToPositiveInfinity, 0xD4C0000000097DE0UL }; + yield return new object[] { 0x37C002682D7CA496UL, 9, MidpointRounding.ToZero, 0x37C002682D7CA496UL }; + yield return new object[] { 0xF800000000000000UL, 12, MidpointRounding.AwayFromZero, 0xF800000000000000UL }; + yield return new object[] { 0x3B0000002F8737D0UL, 5, MidpointRounding.ToZero, 0x3B0000002F8737D0UL }; + yield return new object[] { 0x7800000000000000UL, 12, MidpointRounding.ToZero, 0x7800000000000000UL }; + yield return new object[] { 0xA1A00000000BA750UL, 15, MidpointRounding.ToZero, 0xAFE0000000000000UL }; + yield return new object[] { 0xA380118354CB3560UL, 6, MidpointRounding.ToPositiveInfinity, 0xB100000000000000UL }; + yield return new object[] { 0x51C000118D08CAA9UL, 7, MidpointRounding.ToZero, 0x51C000118D08CAA9UL }; + yield return new object[] { 0x0EE00000000923B9UL, 15, MidpointRounding.ToPositiveInfinity, 0x2FE0000000000001UL }; + yield return new object[] { 0x99C0000000000105UL, 18, MidpointRounding.ToPositiveInfinity, 0xAF80000000000000UL }; + yield return new object[] { 0x13A0000000A6B74AUL, 16, MidpointRounding.ToEven, 0x2FC0000000000000UL }; + yield return new object[] { 0x4D200010544A7EF9UL, 6, MidpointRounding.ToZero, 0x4D200010544A7EF9UL }; + yield return new object[] { 0xAFE129C247897D17UL, 15, MidpointRounding.ToZero, 0xAFE129C247897D17UL }; + yield return new object[] { 0x39E0000018DBDAC4UL, 18, MidpointRounding.ToZero, 0x39E0000018DBDAC4UL }; + yield return new object[] { 0x1CE0466A2E2FA67DUL, 1, MidpointRounding.ToEven, 0x31A0000000000000UL }; + yield return new object[] { 0xD6A000000005F3B6UL, 9, MidpointRounding.AwayFromZero, 0xD6A000000005F3B6UL }; + yield return new object[] { 0x5980000001992B99UL, 9, MidpointRounding.ToNegativeInfinity, 0x5980000001992B99UL }; + yield return new object[] { 0x98801012060B07C9UL, 7, MidpointRounding.ToNegativeInfinity, 0xB0E0000000000001UL }; + yield return new object[] { 0xDFE00000330EA053UL, 1, MidpointRounding.ToPositiveInfinity, 0xDFE00000330EA053UL }; + yield return new object[] { 0x5800000000000000UL, 15, MidpointRounding.ToNegativeInfinity, 0x5800000000000000UL }; + yield return new object[] { 0xA1C0000000000000UL, 9, MidpointRounding.ToNegativeInfinity, 0xB0A0000000000000UL }; + yield return new object[] { 0x31A000F013649DACUL, 10, MidpointRounding.ToZero, 0x31A000F013649DACUL }; + yield return new object[] { 0x59000039FBCD6B26UL, 11, MidpointRounding.ToPositiveInfinity, 0x59000039FBCD6B26UL }; + yield return new object[] { 0xDF200000000000BFUL, 5, MidpointRounding.ToZero, 0xDF200000000000BFUL }; + yield return new object[] { 0xB8600323BC8172A8UL, 11, MidpointRounding.ToZero, 0xB8600323BC8172A8UL }; + yield return new object[] { 0x0E633EA0E618A8C5UL, 1, MidpointRounding.ToZero, 0x31A0000000000000UL }; + yield return new object[] { 0xDF00007875FC51AAUL, 5, MidpointRounding.AwayFromZero, 0xDF00007875FC51AAUL }; + yield return new object[] { 0xBF200000033876D7UL, 5, MidpointRounding.ToPositiveInfinity, 0xBF200000033876D7UL }; + yield return new object[] { 0x8A6000000000282FUL, 13, MidpointRounding.ToZero, 0xB020000000000000UL }; + yield return new object[] { 0x0FA022B13F5EAC78UL, 0, MidpointRounding.ToPositiveInfinity, 0x31C0000000000001UL }; + yield return new object[] { 0xC840000000000000UL, 18, MidpointRounding.ToPositiveInfinity, 0xC840000000000000UL }; + yield return new object[] { 0x00C00006906B8E09UL, 0, MidpointRounding.ToNegativeInfinity, 0x31C0000000000000UL }; + yield return new object[] { 0x3500000000574DB9UL, 5, MidpointRounding.ToEven, 0x3500000000574DB9UL }; + } + + [Theory] + [MemberData(nameof(RoundToDigits_TestData))] + public static void RoundToDigits(ulong value, int digits, MidpointRounding mode, ulong expected) + { + Decimal64 result = Decimal64.Round(Unsafe.BitCast(value), digits, mode); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Fact] + public static void RoundConvenienceOverloads() + { + Decimal64 x = Unsafe.BitCast(0x31A0000000000019UL); // 2.5 + + Assert.Equal(Decimal64.Round(x, 0, MidpointRounding.ToPositiveInfinity), Decimal64.Ceiling(x)); + Assert.Equal(Decimal64.Round(x, 0, MidpointRounding.ToNegativeInfinity), Decimal64.Floor(x)); + Assert.Equal(Decimal64.Round(x, 0, MidpointRounding.ToZero), Decimal64.Truncate(x)); + Assert.Equal(Decimal64.Round(x, 0, MidpointRounding.ToEven), Decimal64.Round(x)); + Assert.Equal(Decimal64.Round(x, 0, MidpointRounding.AwayFromZero), Decimal64.Round(x, MidpointRounding.AwayFromZero)); + Assert.Equal(Decimal64.Round(x, 2, MidpointRounding.ToEven), Decimal64.Round(x, 2)); + } + + [Fact] + public static void IFloatingPoint_ExponentAndSignificand() + { + IFloatingPoint value = Unsafe.BitCast(0x3180000000003039UL); // 123.45 + + Assert.Equal(sizeof(int), value.GetExponentByteCount()); + Assert.Equal(sizeof(ulong), value.GetSignificandByteCount()); + + Span exponent = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentLittleEndian(exponent, out int exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32LittleEndian(exponent)); + + Span significand = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandLittleEndian(significand, out int significandWritten)); + Assert.Equal(sizeof(ulong), significandWritten); + Assert.Equal(12345ul, BinaryPrimitives.ReadUInt64LittleEndian(significand)); + + Assert.Equal(2, value.GetExponentShortestBitLength()); + Assert.Equal(54, value.GetSignificandBitLength()); + + Span exponentBigEndian = stackalloc byte[value.GetExponentByteCount()]; + Assert.True(value.TryWriteExponentBigEndian(exponentBigEndian, out exponentWritten)); + Assert.Equal(sizeof(int), exponentWritten); + Assert.Equal(-2, BinaryPrimitives.ReadInt32BigEndian(exponentBigEndian)); + + Span significandBigEndian = stackalloc byte[value.GetSignificandByteCount()]; + Assert.True(value.TryWriteSignificandBigEndian(significandBigEndian, out significandWritten)); + Assert.Equal(sizeof(ulong), significandWritten); + Assert.Equal(12345ul, BinaryPrimitives.ReadUInt64BigEndian(significandBigEndian)); + + // A non-negative exponent exercises the other GetExponentShortestBitLength branch. + IFloatingPoint integer = Unsafe.BitCast(0x31C0000000003039UL); // 12345 + Assert.Equal(0, integer.GetExponentShortestBitLength()); + + Assert.Equal(123, Decimal64.ConvertToInteger(Unsafe.BitCast(0x3180000000003039UL))); + } + } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalIeee754IntelTestData.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalIeee754IntelTestData.cs index b09102848f230f..155a71063afe89 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalIeee754IntelTestData.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalIeee754IntelTestData.cs @@ -95,6 +95,18 @@ public static class DecimalIeee754IntelTestData private static readonly HashSet s_bid64Cross = new() { "bid32_to_bid64", "bid128_to_bid64" }; private static readonly HashSet s_bid128Cross = new() { "bid32_to_bid128", "bid64_to_bid128" }; + // Truncating remainder (fmod, the C# `%` operator), not the round-to-nearest IEEE 754 remainder. + private static readonly HashSet s_bid32Modulus = new() { "bid32_fmod" }; + private static readonly HashSet s_bid64Modulus = new() { "bid64_fmod" }; + private static readonly HashSet s_bid128Modulus = new() { "bid128_fmod" }; + + // Round to an integral value under each rounding mode, mapping onto the .NET Round/Ceiling/Floor/Truncate + // surface. `round_integral_exact` takes the mode from the rounding-context column, so only its + // round-to-nearest-even (rnd == 0) rows are consumed here; the mode-named variants ignore that column. + private static readonly HashSet s_bid32RoundIntegral = new() { "bid32_round_integral_exact", "bid32_round_integral_nearest_even", "bid32_round_integral_nearest_away", "bid32_round_integral_negative", "bid32_round_integral_positive", "bid32_round_integral_zero" }; + private static readonly HashSet s_bid64RoundIntegral = new() { "bid64_round_integral_exact", "bid64_round_integral_nearest_even", "bid64_round_integral_nearest_away", "bid64_round_integral_negative", "bid64_round_integral_positive", "bid64_round_integral_zero" }; + private static readonly HashSet s_bid128RoundIntegral = new() { "bid128_round_integral_exact", "bid128_round_integral_nearest_even", "bid128_round_integral_nearest_away", "bid128_round_integral_negative", "bid128_round_integral_positive", "bid128_round_integral_zero" }; + /// /// Gets a value indicating whether the Intel readtest.in reference vectors are available, /// gating the theories that consume them. @@ -431,6 +443,74 @@ public static IEnumerable Decimal128Cross() } } + public static IEnumerable Decimal32Modulus() + { + foreach (string[] fields in EnumerateRows(s_bid32Modulus)) + { + if (TryParseBid32(fields[2], out uint left) && TryParseBid32(fields[3], out uint right) && TryParseBid32(fields[4], out uint expected)) + { + yield return new object[] { left, right, expected }; + } + } + } + + public static IEnumerable Decimal64Modulus() + { + foreach (string[] fields in EnumerateRows(s_bid64Modulus)) + { + if (TryParseBid64(fields[2], out ulong left) && TryParseBid64(fields[3], out ulong right) && TryParseBid64(fields[4], out ulong expected)) + { + yield return new object[] { left, right, expected }; + } + } + } + + public static IEnumerable Decimal128Modulus() + { + foreach (string[] fields in EnumerateRows(s_bid128Modulus)) + { + if (TryParseBid128(fields[2], out UInt128 left) && TryParseBid128(fields[3], out UInt128 right) && TryParseBid128(fields[4], out UInt128 expected)) + { + yield return new object[] { left, right, expected }; + } + } + } + + // NaN operands are skipped: rounding leaves the value's payload untouched, but Intel canonicalizes and quiets + // NaNs so its result column would not match the raw operand bits. The mode is taken from the operation name. + public static IEnumerable Decimal32RoundIntegral() + { + foreach (string[] fields in EnumerateRows(s_bid32RoundIntegral)) + { + if (TryParseBid32(fields[2], out uint value) && !IsBid32NaN(value) && TryParseBid32(fields[3], out uint expected)) + { + yield return new object[] { OperationSuffix(fields[0]), value, expected }; + } + } + } + + public static IEnumerable Decimal64RoundIntegral() + { + foreach (string[] fields in EnumerateRows(s_bid64RoundIntegral)) + { + if (TryParseBid64(fields[2], out ulong value) && !IsBid64NaN(value) && TryParseBid64(fields[3], out ulong expected)) + { + yield return new object[] { OperationSuffix(fields[0]), value, expected }; + } + } + } + + public static IEnumerable Decimal128RoundIntegral() + { + foreach (string[] fields in EnumerateRows(s_bid128RoundIntegral)) + { + if (TryParseBid128(fields[2], out UInt128 value) && !IsBid128NaN(value) && TryParseBid128(fields[3], out UInt128 expected)) + { + yield return new object[] { OperationSuffix(fields[0]), value, expected }; + } + } + } + // For `bidNN_from_` the integer source type is the trailing token; for `bidNN_to__int` it is the // third underscore-separated token; for the binary and cross families it is the leading or third token. private static string IntegerSourceType(string operation) => operation.Substring(operation.LastIndexOf('_') + 1);