diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index 2419a1a34585a9..ca41ed84ced12b 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -1664,3 +1664,36 @@ Available at https://github.com/C2SP/wycheproof/blob/2fb7240f471b70bb3aa5cc9b071 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +License notice for Intel Decimal Floating-Point Math Library +------------------------------- + +https://www.intel.com/content/www/us/en/developer/articles/tool/intel-decimal-floating-point-math-library.html + +Copyright (c) 2007-2025, Intel Corp. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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 22e0d7952e3749..1d9af3730bec1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs @@ -1,6 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Text; using System.Diagnostics; using System.Numerics; @@ -75,6 +76,16 @@ internal static DecodedDecimalIeee754 UnpackDecimalIeee754 TDecimal.MaxSignificand) + { + significand = TValue.Zero; + } + return new DecodedDecimalIeee754(signed, biasedExponent - TDecimal.ExponentBias, significand); } @@ -216,6 +227,75 @@ static int InternalInfinityCompare(TValue current, TValue other) } } + /// + /// Determines whether two IEEE 754 decimal values represented by their raw bit patterns + /// are numerically equal using the semantics required by the equality operator. + /// + /// + /// Unlike , which treats NaN as equal + /// to NaN so that equality is consistent with hashing and ordering, this method follows the + /// IEEE 754 equality-comparison rules used by operator ==: NaN is unordered and is + /// therefore never equal to any value, including itself. All other values (including the two + /// zeros and different members of the same cohort) compare by numeric value. + /// + internal static bool EqualsDecimalIeee754(TValue leftDecimalBits, TValue rightDecimalBits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + if (TDecimal.IsNaN(leftDecimalBits) || TDecimal.IsNaN(rightDecimalBits)) + { + return false; + } + + return CompareDecimalIeee754(leftDecimalBits, rightDecimalBits) == 0; + } + + /// + /// Determines the ordering of two IEEE 754 decimal values represented by their raw bit + /// patterns using the semantics required by the relational operators + /// (<, >, <=, >=). + /// + /// + /// A NaN operand is unordered: every relational comparison that involves one is + /// . When neither operand is NaN the values are ordered by numeric + /// value using , so the two zeros + /// compare equal and different members of the same cohort compare equal. + /// + internal static bool LessThanDecimalIeee754(TValue leftDecimalBits, TValue rightDecimalBits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + return !TDecimal.IsNaN(leftDecimalBits) && !TDecimal.IsNaN(rightDecimalBits) + && CompareDecimalIeee754(leftDecimalBits, rightDecimalBits) < 0; + } + + /// + internal static bool GreaterThanDecimalIeee754(TValue leftDecimalBits, TValue rightDecimalBits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + return !TDecimal.IsNaN(leftDecimalBits) && !TDecimal.IsNaN(rightDecimalBits) + && CompareDecimalIeee754(leftDecimalBits, rightDecimalBits) > 0; + } + + /// + internal static bool LessThanOrEqualDecimalIeee754(TValue leftDecimalBits, TValue rightDecimalBits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + return !TDecimal.IsNaN(leftDecimalBits) && !TDecimal.IsNaN(rightDecimalBits) + && CompareDecimalIeee754(leftDecimalBits, rightDecimalBits) <= 0; + } + + /// + internal static bool GreaterThanOrEqualDecimalIeee754(TValue leftDecimalBits, TValue rightDecimalBits) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + return !TDecimal.IsNaN(leftDecimalBits) && !TDecimal.IsNaN(rightDecimalBits) + && CompareDecimalIeee754(leftDecimalBits, rightDecimalBits) >= 0; + } + private static TValue NumberToDecimalIeee754Bits(ref NumberBuffer number) where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo where TValue : unmanaged, IBinaryInteger @@ -254,7 +334,17 @@ private static TValue NumberToDecimalIeee754Bits(ref NumberBuf else if (numberDigitsRemove < number.DigitsCount) { int numberDigitsRemain = number.DigitsCount - numberDigitsRemove; - Debug.Assert(numberDigitsRemain <= TDecimal.Precision); + + if (numberDigitsRemain > TDecimal.Precision) + { + // The coefficient still exceeds the format precision after shifting to the + // minimum quantum, so the value is actually in the normal range rather than + // subnormal. Round the full (exact) digit string to the format precision in a + // single step; this avoids a double rounding and yields a quantum that is at or + // above the minimum adjusted exponent. + numberDigitsRemain = TDecimal.Precision; + } + return DecimalIeee754Rounding(ref number, numberDigitsRemain); } else @@ -341,9 +431,9 @@ private static TValue DecimalIeee754FiniteNumberBinaryEncoding { exponent = TDecimal.MinAdjustedExponent; } - else if (exponent > TDecimal.MaxExponent) + else if (exponent > TDecimal.MaxAdjustedExponent) { - exponent = TDecimal.MaxExponent; + exponent = TDecimal.MaxAdjustedExponent; } } @@ -471,5 +561,818 @@ private static TValue DecimalIeee754Rounding(ref NumberBuffer return DecimalIeee754FiniteNumberBinaryEncoding(number.IsNegative, significand, exponent); } + + /// + /// Produces the quiet NaN that an arithmetic operation propagates from its NaN operands, following the + /// IEEE 754-2019 §6.2.3 recommendation to preserve the payload of the first NaN operand. + /// + /// + /// The first NaN operand ( before ) supplies the sign and + /// payload of the result. A signaling NaN is quieted and a payload that is too large to be canonical + /// (greater than or equal to 10^(Precision - 1)) is discarded, matching the behavior of the Intel + /// reference implementation. + /// + private static TValue PropagateNaN(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // This code is based on `unpack_BID32`, `unpack_BID64`, and `unpack_BID128_value_BLE` 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 + + TValue nanBits = TDecimal.IsNaN(left) ? left : right; + + // The payload occupies the trailing significand field; a value at or above the canonical + // bound is non-canonical and reads as zero. Re-encoding through the NaN combination mask + // clears the signaling bit, yielding a canonical quiet NaN that keeps the operand's sign. + TValue payload = nanBits & ((TValue.One << TDecimal.NumberBitsSignificand) - TValue.One); + + if (payload >= TDecimal.Power10(TDecimal.Precision - 1)) + { + payload = TValue.Zero; + } + + return (nanBits & TDecimal.SignMask) | TDecimal.NaNMask | payload; + } + + /// + /// 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. + /// + /// + /// The two operands are decoded and aligned to their common (smaller) exponent by scaling the larger-exponent + /// coefficient up at double integer width. Digits of the smaller operand that fall below the retained precision + /// are folded into a sticky flag. The aligned coefficients are then added or subtracted with word-level integer + /// arithmetic and fed into the shared rounding path, producing the same result as computing the exact sum. This + /// mirrors the mathematical behavior of the Intel reference implementation. + /// + internal static TValue AddDecimalIeee754(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // This code is based on `bid32_add`, `bid64_add`, and `bid128_add` 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); + } + + if (TDecimal.IsInfinity(left)) + { + // Inf + Inf with opposing signs is invalid (NaN); every other combination + // that includes at least one infinity returns that infinity (canonicalized). + if (TDecimal.IsInfinity(right) && (TDecimal.IsNegative(left) != TDecimal.IsNegative(right))) + { + // An invalid operation produces the canonical quiet NaN, which the Intel reference + // emits with a positive sign and empty payload (`NaNMask`), unlike the negative + // `TDecimal.NaN` constant. + return TDecimal.NaNMask; + } + return TDecimal.IsNegative(left) ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + if (TDecimal.IsInfinity(right)) + { + return TDecimal.IsNegative(right) ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + DecodedDecimalIeee754 a = UnpackDecimalIeee754(left); + DecodedDecimalIeee754 b = UnpackDecimalIeee754(right); + + bool aZero = TValue.IsZero(a.Significand); + bool bZero = TValue.IsZero(b.Significand); + + if (aZero && bZero) + { + // The sum of two zeros keeps the shared sign, otherwise it is +0 under + // round-to-nearest. The preferred exponent for a zero result is the smaller one. + bool zeroSign = a.Signed == b.Signed && a.Signed; + int zeroExponent = Math.Min(a.UnbiasedExponent, b.UnbiasedExponent); + return DecimalIeee754FiniteNumberBinaryEncoding(zeroSign, TValue.Zero, zeroExponent); + } + + if (aZero || bZero) + { + // Adding zero yields the other operand's value, but the preferred exponent is the + // smaller of the two exponents. When the zero's exponent is larger there is nothing + // to do; otherwise the coefficient is padded with trailing zeros (bounded by the + // available precision) to lower the exponent toward the zero's exponent. + DecodedDecimalIeee754 nonZero = aZero ? b : a; + TValue nonZeroBits = aZero ? right : left; + int zeroExponent = aZero ? a.UnbiasedExponent : b.UnbiasedExponent; + + if (zeroExponent >= nonZero.UnbiasedExponent) + { + return nonZeroBits; + } + + int nonZeroDigits = TDecimal.CountDigits(nonZero.Significand); + int pad = Math.Min(nonZero.UnbiasedExponent - zeroExponent, TDecimal.Precision - nonZeroDigits); + TValue paddedSignificand = nonZero.Significand * TDecimal.Power10(pad); + return DecimalIeee754FiniteNumberBinaryEncoding(nonZero.Signed, paddedSignificand, nonZero.UnbiasedExponent - pad); + } + + // Both operands are finite and non-zero. Order them so `hi` has the larger (or equal) + // exponent, then align `lo` to `hi` by scaling `hi` up. The exponent difference is + // capped: anything beyond `guard` extra digits cannot influence the retained precision + // except through rounding, so those low-order digits of `lo` become a sticky flag. + DecodedDecimalIeee754 hi; + DecodedDecimalIeee754 lo; + + if (a.UnbiasedExponent >= b.UnbiasedExponent) + { + hi = a; + lo = b; + } + else + { + hi = b; + lo = a; + } + + int exponentDifference = hi.UnbiasedExponent - lo.UnbiasedExponent; + int guard = TDecimal.Precision + 2; + int effectiveDifference = Math.Min(exponentDifference, guard); + int droppedDigits = exponentDifference - effectiveDifference; + int commonExponent = hi.UnbiasedExponent - effectiveDifference; + + // Align `hi` to the common exponent by scaling its coefficient up by 10^effectiveDifference. The + // scaled coefficient can exceed a single limb (up to ~10^(2*Precision+1)), so it is held at double + // width. The scale factor fits a single limb because effectiveDifference <= Precision + 2. + WideMultiply(hi.Significand, AlignmentScaleFactor(effectiveDifference), out TValue magnitudeHigh, out TValue magnitudeLow); + + // Align `lo` to the common exponent by discarding its `droppedDigits` least-significant digits, which + // fall below the retained range and only contribute stickiness. The retained portion fits a single limb. + bool sticky = false; + TValue loRetained; + + if (droppedDigits >= TDecimal.CountDigits(lo.Significand)) + { + loRetained = TValue.Zero; + sticky = true; + } + else if (droppedDigits > 0) + { + (loRetained, TValue remainder) = TValue.DivRem(lo.Significand, TDecimal.Power10(droppedDigits)); + sticky = !TValue.IsZero(remainder); + } + else + { + loRetained = lo.Significand; + } + + bool sameSign = hi.Signed == lo.Signed; + bool resultSign; + + if (sameSign) + { + // Magnitudes add. Fold the retained `lo` coefficient into the double-width accumulator. + TValue newLow = magnitudeLow + loRetained; + if (newLow < magnitudeLow) + { + magnitudeHigh += TValue.One; + } + magnitudeLow = newLow; + resultSign = hi.Signed; + } + else + { + // Magnitudes subtract. When `droppedDigits > 0` the scaled `hi` coefficient is at least 10^guard, + // which dominates the retained `lo` (< 10^Precision), so `hi` always compares greater in that case. + int comparison = !TValue.IsZero(magnitudeHigh) ? 1 : magnitudeLow.CompareTo(loRetained); + + if (comparison > 0) + { + if (magnitudeLow < loRetained) + { + magnitudeHigh -= TValue.One; + } + magnitudeLow -= loRetained; + resultSign = hi.Signed; + + if (sticky) + { + // The true `lo` magnitude is slightly larger than its retained digits, so the exact + // difference is one unit smaller with a non-zero fractional remainder. + if (TValue.IsZero(magnitudeLow)) + { + magnitudeHigh -= TValue.One; + } + magnitudeLow -= TValue.One; + } + } + else if (comparison < 0) + { + // `droppedDigits` is always zero here, so there is no sticky remainder to account for and both + // magnitudes fit a single limb. + magnitudeLow = loRetained - magnitudeLow; + magnitudeHigh = TValue.Zero; + resultSign = lo.Signed; + } + else + { + // Exact cancellation produces +0 under round-to-nearest. + return DecimalIeee754FiniteNumberBinaryEncoding(false, TValue.Zero, commonExponent); + } + } + + if (TValue.IsZero(magnitudeHigh) && TValue.IsZero(magnitudeLow) && !sticky) + { + return DecimalIeee754FiniteNumberBinaryEncoding(false, TValue.Zero, commonExponent); + } + + return NumberToDecimalIeee754BitsFromWide(resultSign, magnitudeHigh, magnitudeLow, commonExponent, sticky); + } + + /// + /// Subtracts one IEEE 754 decimal value from another, both represented by their raw bit patterns, and + /// returns the bit pattern of the correctly rounded (round-to-nearest, ties-to-even) difference. + /// + /// + /// Subtraction negates the right operand and defers to . The sign of a NaN + /// operand is left untouched so that a NaN result propagates the same payload and sign as addition would. + /// + internal static TValue SubtractDecimalIeee754(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // This code is based on `bid32_sub`, `bid64_sub`, and `bid128_sub` 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(right)) + { + right ^= TDecimal.SignMask; + } + + return AddDecimalIeee754(left, right); + } + + /// + /// Multiplies 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) product. + /// + /// + /// The operands are decoded and their coefficients multiplied exactly using a double-width integer product + /// (the product can require up to twice the format precision). The exact product exponent is the sum of the + /// operand exponents, which is also the IEEE 754 preferred exponent because trailing zeros of the product are + /// retained. The exact product is then fed into the shared word-level rounding path. This mirrors the + /// mathematical behavior of the Intel reference implementation. + /// + internal static TValue MultiplyDecimalIeee754(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // This code is based on `bid32_mul`, `bid64_mul`, and `bid128_mul` 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 sign of a product is always the exclusive-or of the operand signs, including zeros. + bool resultSign = TDecimal.IsNegative(left) ^ TDecimal.IsNegative(right); + + bool leftInfinity = TDecimal.IsInfinity(left); + bool rightInfinity = TDecimal.IsInfinity(right); + + if (leftInfinity || rightInfinity) + { + // Infinity multiplied by zero is invalid (NaN); any other product involving an infinity is + // an infinity carrying the exclusive-or sign. + bool otherZero = (leftInfinity && !rightInfinity && TValue.IsZero(UnpackDecimalIeee754(right).Significand)) + || (rightInfinity && !leftInfinity && TValue.IsZero(UnpackDecimalIeee754(left).Significand)); + + if (otherZero) + { + // An invalid operation produces the canonical quiet NaN, which the Intel reference + // emits with a positive sign and empty payload (`NaNMask`), unlike the negative + // `TDecimal.NaN` constant. + return TDecimal.NaNMask; + } + + return resultSign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + DecodedDecimalIeee754 a = UnpackDecimalIeee754(left); + DecodedDecimalIeee754 b = UnpackDecimalIeee754(right); + + int productExponent = a.UnbiasedExponent + b.UnbiasedExponent; + + if (TValue.IsZero(a.Significand) || TValue.IsZero(b.Significand)) + { + // Zero times a finite value is zero. The preferred exponent is the sum of the operand + // exponents (clamped to the representable range by the encoder). + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, TValue.Zero, productExponent); + } + + // The exact product of two coefficients needs up to twice the format precision, so it is computed at + // double the integer width. The sum of the operand exponents is also the IEEE 754 preferred exponent + // (trailing zeros of the product are retained), so it is fed unchanged into the shared word-level + // rounding/encoding path. + WideMultiply(a.Significand, b.Significand, out TValue productHigh, out TValue productLow); + + return NumberToDecimalIeee754BitsFromWide(resultSign, productHigh, productLow, productExponent, sticky: false); + } + + /// + /// Divides 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) quotient. + /// + /// + /// The dividend coefficient is scaled up by a power of ten large enough to expose more than the format + /// precision in significant quotient digits, then long-divided by the divisor coefficient one decimal digit + /// at a time. The running remainder and the quotient each stay within a single limb, so the division uses only + /// word-level integer arithmetic. The final remainder determines whether the result is exact; an inexact + /// result carries a sticky flag into the shared rounding path, while an exact result has its exponent raised + /// toward the IEEE 754 preferred exponent (the difference of the operand exponents) by discarding trailing + /// zeros. This mirrors the mathematical behavior of the Intel reference implementation. + /// + internal static TValue DivideDecimalIeee754(TValue left, TValue right) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // This code is based on `bid32_div`, `bid64_div`, and `bid128_div` 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 sign of a quotient is always the exclusive-or of the operand signs, including zeros. + bool resultSign = TDecimal.IsNegative(left) ^ TDecimal.IsNegative(right); + + if (TDecimal.IsInfinity(left)) + { + // Infinity divided by infinity is invalid (NaN); infinity divided by any finite value is + // an infinity carrying the exclusive-or sign. + if (TDecimal.IsInfinity(right)) + { + // An invalid operation produces the canonical quiet NaN, which the Intel reference + // emits with a positive sign and empty payload (`NaNMask`), unlike the negative + // `TDecimal.NaN` constant. + return TDecimal.NaNMask; + } + + return resultSign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + if (TDecimal.IsInfinity(right)) + { + // A finite value divided by infinity is zero with the exclusive-or sign; the preferred + // exponent is the minimum (the encoder clamps the sub-minimum exponent up to it). + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, TValue.Zero, TDecimal.MinAdjustedExponent); + } + + DecodedDecimalIeee754 a = UnpackDecimalIeee754(left); + DecodedDecimalIeee754 b = UnpackDecimalIeee754(right); + + if (TValue.IsZero(b.Significand)) + { + // Zero divided by zero is invalid (NaN); any other value divided by zero is an infinity + // carrying the exclusive-or sign (division by zero). + if (TValue.IsZero(a.Significand)) + { + // An invalid operation produces the canonical quiet NaN, which the Intel reference + // emits with a positive sign and empty payload (`NaNMask`), unlike the negative + // `TDecimal.NaN` constant. + return TDecimal.NaNMask; + } + + return resultSign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + if (TValue.IsZero(a.Significand)) + { + // Zero divided by a finite non-zero value is zero. The preferred exponent is the difference + // of the operand exponents (clamped to the representable range by the encoder). + return DecimalIeee754FiniteNumberBinaryEncoding(resultSign, TValue.Zero, a.UnbiasedExponent - b.UnbiasedExponent); + } + + int dividendDigits = TDecimal.CountDigits(a.Significand); + int divisorDigits = TDecimal.CountDigits(b.Significand); + + // Scale the dividend up so the quotient exposes at least Precision + 1 significant digits. + // Because both coefficients have at most Precision digits, this shift is always positive. + int shift = divisorDigits - dividendDigits + TDecimal.Precision + 1; + Debug.Assert(shift > 0); + + int quotientExponent = a.UnbiasedExponent - b.UnbiasedExponent - shift; + + // Long-divide `a.Significand * 10^shift` by `b.Significand` one decimal digit at a time. The running + // remainder always stays below the divisor and the quotient always stays below 10^(Precision + 2), so + // every intermediate value fits a single limb and no wide integer is needed for division. The final + // remainder determines whether the quotient is exact. + TValue divisor = b.Significand; + TValue ten = TValue.CreateTruncating(10); + (TValue quotient, TValue remainder) = TValue.DivRem(a.Significand, divisor); + + for (int i = 0; i < shift; i++) + { + remainder *= ten; + (TValue digit, remainder) = TValue.DivRem(remainder, divisor); + quotient = (quotient * ten) + digit; + } + + bool remainderNonZero = !TValue.IsZero(remainder); + + if (!remainderNonZero) + { + // The quotient is exact, so raise its exponent toward the preferred exponent (the difference + // of the operand exponents) by discarding trailing zeros. + int idealExponent = a.UnbiasedExponent - b.UnbiasedExponent; + + while (quotientExponent < idealExponent) + { + (TValue stripped, TValue digit) = TValue.DivRem(quotient, ten); + + if (!TValue.IsZero(digit)) + { + break; + } + + quotient = stripped; + quotientExponent++; + } + } + + return NumberToDecimalIeee754BitsFromWide(resultSign, TValue.Zero, quotient, quotientExponent, remainderNonZero); + } + + /// + /// 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, + /// matching the existing parsing/formatting paths. The slightly larger alignment exponents + /// (Precision..Precision + 2, which the table does not cover) reduce the exponent by the largest table + /// entry each iteration and finish with a single lookup for the remainder. The result always fits a single limb + /// for every supported format. + /// + private static TValue AlignmentScaleFactor(int exponent) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + int highestTableExponent = TDecimal.Precision - 1; + TValue largest = TDecimal.Power10(highestTableExponent); + TValue result = TValue.One; + + while (exponent > highestTableExponent) + { + result *= largest; + exponent -= highestTableExponent; + } + + return result * TDecimal.Power10(exponent); + } + + /// + /// Computes the full (double-width) product of two significands as a pair of + /// limbs ( holds the more significant half). The product of two coefficients can + /// require up to twice the format precision, which always fits in two limbs of the underlying integer width. + /// + private static void WideMultiply(TValue left, TValue right, out TValue high, out TValue low) + where TValue : unmanaged, IBinaryInteger + { + // For the 32-bit and 64-bit formats the full product fits in a single wider C# integer (ulong and + // UInt128 respectively), so it comes from one native multiply. The 128-bit format has no wider + // native type and uses the schoolbook half-limb decomposition below. + if (typeof(TValue) == typeof(uint)) + { + ulong product = (ulong)uint.CreateTruncating(left) * uint.CreateTruncating(right); + high = TValue.CreateTruncating(product >> 32); + low = TValue.CreateTruncating(product); + return; + } + else if (typeof(TValue) == typeof(ulong)) + { + UInt128 product = (UInt128)ulong.CreateTruncating(left) * ulong.CreateTruncating(right); + high = TValue.CreateTruncating(product >> 64); + low = TValue.CreateTruncating(product); + return; + } + + int bits = TValue.Zero.GetByteCount() * 8; + int half = bits / 2; + TValue lowMask = (TValue.One << half) - TValue.One; + + TValue leftLow = left & lowMask; + TValue leftHigh = left >> half; + TValue rightLow = right & lowMask; + TValue rightHigh = right >> half; + + TValue lowLow = leftLow * rightLow; + TValue lowHigh = leftLow * rightHigh; + TValue highLow = leftHigh * rightLow; + TValue highHigh = leftHigh * rightHigh; + + TValue cross = (lowLow >> half) + (lowHigh & lowMask) + (highLow & lowMask); + + low = (lowLow & lowMask) | ((cross & lowMask) << half); + high = highHigh + (lowHigh >> half) + (highLow >> half) + (cross >> half); + } + + /// + /// Divides the double-width value in (, ) by ten in place, + /// returning the discarded least-significant decimal digit. Used to strip low-order digits during rounding. + /// + /// + /// Only the 128-bit format reaches this helper: the 32-bit and 64-bit formats widen the limb pair to a + /// single native integer and divide directly (see and + /// ). The Intel reference implementation avoids hardware + /// division here by multiplying with precomputed reciprocals of powers of ten (e.g. + /// bid_reciprocals10_64) and shifting; this helper instead uses direct integer division for + /// simplicity, and adopting the reciprocal-multiply tables is a possible future performance optimization. + /// + private static int WideDivideByTen(ref TValue high, ref TValue low) + where TValue : unmanaged, IBinaryInteger + { + TValue ten = TValue.CreateTruncating(10); + + if (TValue.IsZero(high)) + { + (TValue quotient, TValue remainder) = TValue.DivRem(low, ten); + low = quotient; + return int.CreateTruncating(remainder); + } + + int bits = TValue.Zero.GetByteCount() * 8; + int half = bits / 2; + TValue lowMask = (TValue.One << half) - TValue.One; + TValue baseValue = TValue.One << half; + + // Long division of the four half-width limbs (most significant first) by ten. Because the running + // remainder stays below ten, `remainder * baseValue + limb` never exceeds the integer width. + TValue rem = TValue.Zero; + (TValue q3, rem) = TValue.DivRem((rem * baseValue) + (high >> half), ten); + (TValue q2, rem) = TValue.DivRem((rem * baseValue) + (high & lowMask), ten); + (TValue q1, rem) = TValue.DivRem((rem * baseValue) + (low >> half), ten); + (TValue q0, rem) = TValue.DivRem((rem * baseValue) + (low & lowMask), ten); + + high = (q3 << half) | q2; + low = (q1 << half) | q0; + return int.CreateTruncating(rem); + } + + /// + /// Counts the number of decimal digits in the double-width value (, ), + /// which must be non-zero. + /// + private static int WideDigitCount(TValue high, TValue low) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + // For the 32-bit and 64-bit formats the (high, low) limb pair fits in a single wider C# integer + // (ulong and UInt128 respectively), so the digit count comes straight from the existing helpers + // instead of stripping the high limb a digit at a time. The 128-bit format has no wider native + // type and falls back to the generic limb loop below. + if (typeof(TValue) == typeof(uint)) + { + ulong wide = ((ulong)uint.CreateTruncating(high) << 32) | uint.CreateTruncating(low); + return FormattingHelpers.CountDigits(wide); + } + else if (typeof(TValue) == typeof(ulong)) + { + UInt128 wide = ((UInt128)ulong.CreateTruncating(high) << 64) | ulong.CreateTruncating(low); + return FormattingHelpers.CountDigits(wide); + } + + int count = 0; + + while (!TValue.IsZero(high)) + { + WideDivideByTen(ref high, ref low); + count++; + } + + return count + TDecimal.CountDigits(low); + } + + /// + /// Removes the least-significant decimal digits from the double-width value, + /// returning the retained significand (which fits in a single limb). The most-significant removed digit is + /// returned in for the rounding decision; all lower removed digits are folded + /// into . + /// + private static TValue DropDigits(ref TValue high, ref TValue low, int dropCount, ref bool sticky, out int roundDigit) + where TValue : unmanaged, IBinaryInteger + { + roundDigit = 0; + + if (dropCount == 0) + { + return low; + } + + // For the 32-bit and 64-bit formats the (high, low) limb pair fits in a single wider C# integer + // (ulong and UInt128 respectively), so the requested digits are dropped with one native division + // by 10^dropCount rather than a per-digit long-division loop. The remainder holds the removed + // low-order digits: its most-significant digit is the rounding digit and everything below it is + // folded into the sticky bit. The 128-bit format has no wider native type and uses the generic + // limb loop below. + if (typeof(TValue) == typeof(uint)) + { + ulong wide = ((ulong)uint.CreateTruncating(high) << 32) | uint.CreateTruncating(low); + + ulong scale = 1; + for (int i = 1; i < dropCount; i++) + { + scale *= 10; + } + ulong power = scale * 10; + + (ulong quotient, ulong remainder) = ulong.DivRem(wide, power); + roundDigit = int.CreateTruncating(remainder / scale); + sticky |= (remainder % scale) != 0; + + high = TValue.Zero; + low = TValue.CreateTruncating(quotient); + return low; + } + else if (typeof(TValue) == typeof(ulong)) + { + UInt128 wide = ((UInt128)ulong.CreateTruncating(high) << 64) | ulong.CreateTruncating(low); + + UInt128 scale = UInt128.One; + for (int i = 1; i < dropCount; i++) + { + scale *= 10; + } + UInt128 power = scale * 10; + + (UInt128 quotient, UInt128 remainder) = UInt128.DivRem(wide, power); + roundDigit = int.CreateTruncating(remainder / scale); + sticky |= (remainder % scale) != UInt128.Zero; + + high = TValue.Zero; + low = TValue.CreateTruncating(quotient); + return low; + } + + for (int i = 0; i < dropCount; i++) + { + if (i > 0) + { + sticky |= roundDigit != 0; + } + + roundDigit = WideDivideByTen(ref high, ref low); + } + + Debug.Assert(TValue.IsZero(high)); + return low; + } + + /// + /// Rounds the exact double-width coefficient (, ) with value + /// ±coefficient·10^ (plus an optional sub-unit tail) + /// to the nearest representable IEEE 754 decimal value using round-to-nearest, ties-to-even, and returns its + /// BID bit pattern. This is the integer-based counterpart of + /// used by the arithmetic operators; it mirrors the same exponent-range handling without materializing a digit string. + /// + private static TValue NumberToDecimalIeee754BitsFromWide(bool sign, TValue high, TValue low, int exponent, bool sticky) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + if (TValue.IsZero(high) && TValue.IsZero(low)) + { + return DecimalIeee754FiniteNumberBinaryEncoding(sign, TValue.Zero, exponent); + } + + int precision = TDecimal.Precision; + + if (exponent > TDecimal.MaxExponent) + { + return sign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + int digitsCount = WideDigitCount(high, low); + + if (exponent > TDecimal.MaxAdjustedExponent) + { + // The least-significant digit already sits above the largest representable quantum. The value is + // representable only if the coefficient can be padded with trailing zeros without exceeding the + // precision; otherwise it overflows to infinity. + int numberZeroDigits = exponent - TDecimal.MaxAdjustedExponent; + + if (digitsCount + numberZeroDigits > precision) + { + return sign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + TValue paddedSignificand = low * TDecimal.Power10(numberZeroDigits); + return DecimalIeee754FiniteNumberBinaryEncoding(sign, paddedSignificand, TDecimal.MaxAdjustedExponent); + } + + if (exponent < TDecimal.MinAdjustedExponent) + { + int numberDigitsRemove = TDecimal.MinAdjustedExponent - exponent; + + if (numberDigitsRemove > digitsCount) + { + return DecimalIeee754FiniteNumberBinaryEncoding(sign, TValue.Zero, TDecimal.MinAdjustedExponent); + } + else if (numberDigitsRemove < digitsCount) + { + int numberDigitsRemain = digitsCount - numberDigitsRemove; + + if (numberDigitsRemain > precision) + { + // Still above the format precision after shifting to the minimum quantum, so the value is + // actually normal. Round to the precision in a single step to avoid a double rounding. + numberDigitsRemain = precision; + } + + return RoundWideToSignificand(sign, high, low, digitsCount, exponent, numberDigitsRemain, sticky); + } + else + { + return RoundWideToZeroOrEpsilon(sign, high, low, digitsCount, sticky); + } + } + + if (digitsCount > precision) + { + int numberDigitsRemove = digitsCount - precision; + + if (exponent + numberDigitsRemove > TDecimal.MaxAdjustedExponent) + { + return sign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + return RoundWideToSignificand(sign, high, low, digitsCount, exponent, precision, sticky); + } + + return DecimalIeee754FiniteNumberBinaryEncoding(sign, low, exponent); + } + + /// + /// Rounds the double-width coefficient to significant digits using + /// round-to-nearest, ties-to-even, and encodes the result. This is the integer-based counterpart of + /// . + /// + private static TValue RoundWideToSignificand(bool sign, TValue high, TValue low, int digitsCount, int exponent, int numberDigitsRemain, bool sticky) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + int dropCount = digitsCount - numberDigitsRemain; + TValue significand = DropDigits(ref high, ref low, dropCount, ref sticky, out int roundDigit); + int resultExponent = exponent + dropCount; + + bool roundUp = (roundDigit > 5) + || ((roundDigit == 5) && (sticky || TValue.IsOddInteger(significand))); + + if (!roundUp) + { + return DecimalIeee754FiniteNumberBinaryEncoding(sign, significand, resultExponent); + } + + if (significand == TDecimal.MaxSignificand) + { + resultExponent += 1; + + if (resultExponent > TDecimal.MaxAdjustedExponent) + { + return sign ? TDecimal.NegativeInfinity : TDecimal.PositiveInfinity; + } + + significand = TDecimal.Power10(TDecimal.Precision - 1); + return DecimalIeee754FiniteNumberBinaryEncoding(sign, significand, resultExponent); + } + + significand += TValue.One; + return DecimalIeee754FiniteNumberBinaryEncoding(sign, significand, resultExponent); + } + + /// + /// Rounds a double-width coefficient whose value lies below the minimum quantum to either zero or the + /// smallest representable subnormal (epsilon), using round-to-nearest, ties-to-even. This is the + /// integer-based counterpart of . + /// + private static TValue RoundWideToZeroOrEpsilon(bool sign, TValue high, TValue low, int digitsCount, bool sticky) + where TDecimal : unmanaged, IDecimalIeee754ParseAndFormatInfo + where TValue : unmanaged, IBinaryInteger + { + TValue lead = DropDigits(ref high, ref low, digitsCount - 1, ref sticky, out int roundDigit); + bool restNonZero = sticky || (roundDigit != 0); + int leadDigit = int.CreateTruncating(lead); + + TValue significand = ((leadDigit > 5) || ((leadDigit == 5) && restNonZero)) ? TValue.One : TValue.Zero; + return DecimalIeee754FiniteNumberBinaryEncoding(sign, significand, TDecimal.MinAdjustedExponent); + } } } 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 721d96bcd297f9..363ef5e1f15158 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs @@ -33,6 +33,8 @@ public readonly struct Decimal128 // which stores zero with the biased exponent rather than the minimum exponent. private static UInt128 ZeroValue => new UInt128(0x3040_0000_0000_0000, 0); private static UInt128 NegativeZeroValue => new UInt128(0xB040_0000_0000_0000, 0); + // One (+1 * 10^0) shares the biased exponent of canonical zero with a coefficient of one. + private static UInt128 OneValue => new UInt128(0x3040_0000_0000_0000, 1); private static UInt128 QuietNaNValue => new UInt128(0xFC00_0000_0000_0000, 0); private const ulong SignMaskUpper = 0x8000_0000_0000_0000; @@ -246,6 +248,128 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin return Number.FormatDecimalIeee754(new UInt128(_upper, _lower), format, NumberFormatInfo.GetInstance(provider)); } + /// Computes the unary plus of a value. + /// The value for which to compute the unary plus. + /// unchanged. + public static Decimal128 operator +(Decimal128 value) => value; + + /// Computes the unary negation of a value. + /// The value for which to compute the unary negation. + /// The unary negation of . + public static Decimal128 operator -(Decimal128 value) => new Decimal128(value._upper ^ SignMaskUpper, value._lower); + + /// Increments a value. + /// The value to increment. + /// The result of incrementing by one. + public static Decimal128 operator ++(Decimal128 value) + { + UInt128 result = Number.AddDecimalIeee754(new UInt128(value._upper, value._lower), OneValue); + return new Decimal128(result); + } + + /// Decrements a value. + /// The value to decrement. + /// The result of decrementing by one. + public static Decimal128 operator --(Decimal128 value) + { + UInt128 result = Number.SubtractDecimalIeee754(new UInt128(value._upper, value._lower), OneValue); + return new Decimal128(result); + } + + /// Adds two values together to compute their sum. + /// The value to which is added. + /// The value which is added to . + /// The sum of and . + public static Decimal128 operator +(Decimal128 left, Decimal128 right) + { + UInt128 result = Number.AddDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + return new Decimal128(result); + } + + /// Subtracts two values to compute their difference. + /// The value from which is subtracted. + /// The value which is subtracted from . + /// The difference of subtracted from . + public static Decimal128 operator -(Decimal128 left, Decimal128 right) + { + UInt128 result = Number.SubtractDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + return new Decimal128(result); + } + + /// Multiplies two values together to compute their product. + /// The value which multiplies. + /// The value which multiplies . + /// The product of and . + public static Decimal128 operator *(Decimal128 left, Decimal128 right) + { + UInt128 result = Number.MultiplyDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + return new Decimal128(result); + } + + /// Divides two values together to compute their quotient. + /// The value which divides. + /// The value which divides . + /// The quotient of divided by . + public static Decimal128 operator /(Decimal128 left, Decimal128 right) + { + UInt128 result = Number.DivideDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + return new Decimal128(result); + } + + /// Compares two values to determine equality. + /// The value to compare with . + /// The value to compare with . + /// true if is equal to ; otherwise, false. + public static bool operator ==(Decimal128 left, Decimal128 right) + { + return Number.EqualsDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + } + + /// Compares two values to determine inequality. + /// The value to compare with . + /// The value to compare with . + /// true if is not equal to ; otherwise, false. + public static bool operator !=(Decimal128 left, Decimal128 right) + { + return !(left == right); + } + + /// Compares two values to determine which is less. + /// The value to compare with . + /// The value to compare with . + /// true if is less than ; otherwise, false. + public static bool operator <(Decimal128 left, Decimal128 right) + { + return Number.LessThanDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + } + + /// Compares two values to determine which is greater. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than ; otherwise, false. + public static bool operator >(Decimal128 left, Decimal128 right) + { + return Number.GreaterThanDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + } + + /// Compares two values to determine which is less or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is less than or equal to ; otherwise, false. + public static bool operator <=(Decimal128 left, Decimal128 right) + { + return Number.LessThanOrEqualDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + } + + /// Compares two values to determine which is greater or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than or equal to ; otherwise, false. + public static bool operator >=(Decimal128 left, Decimal128 right) + { + return Number.GreaterThanOrEqualDecimalIeee754(new UInt128(left._upper, left._lower), new UInt128(right._upper, right._lower)); + } + // // INumberBase // 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 d5f409cac95f4c..6209691ee7390e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal32.cs @@ -34,6 +34,8 @@ internal Decimal32(uint value) // which stores zero with the biased exponent rather than the minimum exponent. private const uint ZeroValue = 0x3280_0000; private const uint NegativeZeroValue = 0xB280_0000; + // One (+1 * 10^0) shares the biased exponent of canonical zero with a coefficient of one. + private const uint OneValue = ZeroValue | 0x1; private const uint QuietNaNValue = 0xFC00_0000; private const uint G0G1Mask = 0x6000_0000; private const uint SignMask = 0x8000_0000; @@ -252,6 +254,122 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin return Number.FormatDecimalIeee754(_value, format, NumberFormatInfo.GetInstance(provider)); } + /// Computes the unary plus of a value. + /// The value for which to compute the unary plus. + /// unchanged. + public static Decimal32 operator +(Decimal32 value) => value; + + /// Computes the unary negation of a value. + /// The value for which to compute the unary negation. + /// The unary negation of . + public static Decimal32 operator -(Decimal32 value) => new Decimal32(value._value ^ SignMask); + + /// Increments a value. + /// The value to increment. + /// The result of incrementing by one. + public static Decimal32 operator ++(Decimal32 value) + { + return new Decimal32(Number.AddDecimalIeee754(value._value, OneValue)); + } + + /// Decrements a value. + /// The value to decrement. + /// The result of decrementing by one. + public static Decimal32 operator --(Decimal32 value) + { + return new Decimal32(Number.SubtractDecimalIeee754(value._value, OneValue)); + } + + /// Adds two values together to compute their sum. + /// The value to which is added. + /// The value which is added to . + /// The sum of and . + public static Decimal32 operator +(Decimal32 left, Decimal32 right) + { + return new Decimal32(Number.AddDecimalIeee754(left._value, right._value)); + } + + /// Subtracts two values to compute their difference. + /// The value from which is subtracted. + /// The value which is subtracted from . + /// The difference of subtracted from . + public static Decimal32 operator -(Decimal32 left, Decimal32 right) + { + return new Decimal32(Number.SubtractDecimalIeee754(left._value, right._value)); + } + + /// Multiplies two values together to compute their product. + /// The value which multiplies. + /// The value which multiplies . + /// The product of and . + public static Decimal32 operator *(Decimal32 left, Decimal32 right) + { + return new Decimal32(Number.MultiplyDecimalIeee754(left._value, right._value)); + } + + /// Divides two values together to compute their quotient. + /// The value which divides. + /// The value which divides . + /// The quotient of divided by . + public static Decimal32 operator /(Decimal32 left, Decimal32 right) + { + return new Decimal32(Number.DivideDecimalIeee754(left._value, right._value)); + } + + /// Compares two values to determine equality. + /// The value to compare with . + /// The value to compare with . + /// true if is equal to ; otherwise, false. + public static bool operator ==(Decimal32 left, Decimal32 right) + { + return Number.EqualsDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine inequality. + /// The value to compare with . + /// The value to compare with . + /// true if is not equal to ; otherwise, false. + public static bool operator !=(Decimal32 left, Decimal32 right) + { + return !(left == right); + } + + /// Compares two values to determine which is less. + /// The value to compare with . + /// The value to compare with . + /// true if is less than ; otherwise, false. + public static bool operator <(Decimal32 left, Decimal32 right) + { + return Number.LessThanDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is greater. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than ; otherwise, false. + public static bool operator >(Decimal32 left, Decimal32 right) + { + return Number.GreaterThanDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is less or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is less than or equal to ; otherwise, false. + public static bool operator <=(Decimal32 left, Decimal32 right) + { + return Number.LessThanOrEqualDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is greater or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than or equal to ; otherwise, false. + public static bool operator >=(Decimal32 left, Decimal32 right) + { + return Number.GreaterThanOrEqualDecimalIeee754(left._value, right._value); + } + // // INumberBase // 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 67f9806814b01b..dba4d7a1967699 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal64.cs @@ -27,6 +27,8 @@ public readonly struct Decimal64 // which stores zero with the biased exponent rather than the minimum exponent. private const ulong ZeroValue = 0x31C0_0000_0000_0000; private const ulong NegativeZeroValue = 0xB1C0_0000_0000_0000; + // One (+1 * 10^0) shares the biased exponent of canonical zero with a coefficient of one. + private const ulong OneValue = ZeroValue | 0x1; private const ulong QuietNaNValue = 0xFC00_0000_0000_0000; private const ulong G0G1Mask = 0x6000_0000_0000_0000; private const ulong SignMask = 0x8000_0000_0000_0000; @@ -253,6 +255,122 @@ public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] strin return Number.FormatDecimalIeee754(_value, format, NumberFormatInfo.GetInstance(provider)); } + /// Computes the unary plus of a value. + /// The value for which to compute the unary plus. + /// unchanged. + public static Decimal64 operator +(Decimal64 value) => value; + + /// Computes the unary negation of a value. + /// The value for which to compute the unary negation. + /// The unary negation of . + public static Decimal64 operator -(Decimal64 value) => new Decimal64(value._value ^ SignMask); + + /// Increments a value. + /// The value to increment. + /// The result of incrementing by one. + public static Decimal64 operator ++(Decimal64 value) + { + return new Decimal64(Number.AddDecimalIeee754(value._value, OneValue)); + } + + /// Decrements a value. + /// The value to decrement. + /// The result of decrementing by one. + public static Decimal64 operator --(Decimal64 value) + { + return new Decimal64(Number.SubtractDecimalIeee754(value._value, OneValue)); + } + + /// Adds two values together to compute their sum. + /// The value to which is added. + /// The value which is added to . + /// The sum of and . + public static Decimal64 operator +(Decimal64 left, Decimal64 right) + { + return new Decimal64(Number.AddDecimalIeee754(left._value, right._value)); + } + + /// Subtracts two values to compute their difference. + /// The value from which is subtracted. + /// The value which is subtracted from . + /// The difference of subtracted from . + public static Decimal64 operator -(Decimal64 left, Decimal64 right) + { + return new Decimal64(Number.SubtractDecimalIeee754(left._value, right._value)); + } + + /// Multiplies two values together to compute their product. + /// The value which multiplies. + /// The value which multiplies . + /// The product of and . + public static Decimal64 operator *(Decimal64 left, Decimal64 right) + { + return new Decimal64(Number.MultiplyDecimalIeee754(left._value, right._value)); + } + + /// Divides two values together to compute their quotient. + /// The value which divides. + /// The value which divides . + /// The quotient of divided by . + public static Decimal64 operator /(Decimal64 left, Decimal64 right) + { + return new Decimal64(Number.DivideDecimalIeee754(left._value, right._value)); + } + + /// Compares two values to determine equality. + /// The value to compare with . + /// The value to compare with . + /// true if is equal to ; otherwise, false. + public static bool operator ==(Decimal64 left, Decimal64 right) + { + return Number.EqualsDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine inequality. + /// The value to compare with . + /// The value to compare with . + /// true if is not equal to ; otherwise, false. + public static bool operator !=(Decimal64 left, Decimal64 right) + { + return !(left == right); + } + + /// Compares two values to determine which is less. + /// The value to compare with . + /// The value to compare with . + /// true if is less than ; otherwise, false. + public static bool operator <(Decimal64 left, Decimal64 right) + { + return Number.LessThanDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is greater. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than ; otherwise, false. + public static bool operator >(Decimal64 left, Decimal64 right) + { + return Number.GreaterThanDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is less or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is less than or equal to ; otherwise, false. + public static bool operator <=(Decimal64 left, Decimal64 right) + { + return Number.LessThanOrEqualDecimalIeee754(left._value, right._value); + } + + /// Compares two values to determine which is greater or equal. + /// The value to compare with . + /// The value to compare with . + /// true if is greater than or equal to ; otherwise, false. + public static bool operator >=(Decimal64 left, Decimal64 right) + { + return Number.GreaterThanOrEqualDecimalIeee754(left._value, right._value); + } + // // INumberBase // diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index bde71d9b0127d0..62fc8aefc1d5e5 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -11418,136 +11418,157 @@ public static void HtmlEncode(string? value, System.IO.TextWriter output) { } } namespace System.Numerics { - public readonly struct Decimal32 - : System.IComparable, - System.IComparable, - System.IEquatable, - System.ISpanParsable, - System.Numerics.IMinMaxValue + public readonly partial struct Decimal128 : System.IComparable, System.IComparable, System.IEquatable, System.IParsable, System.ISpanParsable, System.Numerics.IMinMaxValue { + private readonly int _dummyPrimitive; + public static System.Numerics.Decimal128 Epsilon { get { throw null; } } + public static System.Numerics.Decimal128 MaxValue { get { throw null; } } + public static System.Numerics.Decimal128 MinValue { get { throw null; } } + public static System.Numerics.Decimal128 NaN { get { throw null; } } + public static System.Numerics.Decimal128 NegativeInfinity { get { throw null; } } + public static System.Numerics.Decimal128 NegativeZero { get { throw null; } } + public static System.Numerics.Decimal128 PositiveInfinity { get { throw null; } } + public static System.Numerics.Decimal128 Zero { get { throw null; } } + public int CompareTo(System.Numerics.Decimal128 other) { throw null; } public int CompareTo(object? value) { throw null; } - public int CompareTo(Decimal32 other) { throw null; } - public bool Equals(Decimal32 other) { throw null; } - - public static Decimal32 Parse(string s) { throw null; } - public static Decimal32 Parse(string s, System.Globalization.NumberStyles style) { throw null; } - public static Decimal32 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } - public static Decimal32 Parse(string s, IFormatProvider? provider) { throw null; } - public static Decimal32 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, IFormatProvider? provider = null) { throw null; } - public static Decimal32 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; } - - public static Decimal32 NaN { get { throw null; } } - public static Decimal32 NegativeInfinity { get { throw null; } } - public static Decimal32 PositiveInfinity { get { throw null; } } - public static Decimal32 NegativeZero { get { throw null; } } - public static Decimal32 Zero { get { throw null; } } - public static Decimal32 MaxValue { get { throw null; } } - public static Decimal32 MinValue { get { throw null; } } - - public static Decimal32 Epsilon { get { 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 override int GetHashCode() { 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; } + public static System.Numerics.Decimal128 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 bool operator >=(System.Numerics.Decimal128 left, System.Numerics.Decimal128 right) { throw null; } + public static System.Numerics.Decimal128 operator ++(System.Numerics.Decimal128 value) { 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 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 value) { throw null; } + public static System.Numerics.Decimal128 operator +(System.Numerics.Decimal128 value) { throw null; } + public static System.Numerics.Decimal128 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.Decimal128 Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal128 Parse(string s) { throw null; } + 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 override string ToString() { throw null; } public string ToString(System.IFormatProvider? provider) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format, System.IFormatProvider? provider) { throw null; } - - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal32 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, out Decimal32 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal32 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int bytesConsumed) { 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 bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, out System.Numerics.Decimal128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal128 result) { throw null; } + public static bool TryParse([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 static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Numerics.Decimal128 result) { throw null; } } - public readonly struct Decimal64 - : System.IComparable, - System.IComparable, - System.IEquatable, - System.ISpanParsable, - System.Numerics.IMinMaxValue + public readonly partial struct Decimal32 : System.IComparable, System.IComparable, System.IEquatable, System.IParsable, System.ISpanParsable, System.Numerics.IMinMaxValue { + private readonly int _dummyPrimitive; + public static System.Numerics.Decimal32 Epsilon { get { throw null; } } + public static System.Numerics.Decimal32 MaxValue { get { throw null; } } + public static System.Numerics.Decimal32 MinValue { get { throw null; } } + public static System.Numerics.Decimal32 NaN { get { throw null; } } + public static System.Numerics.Decimal32 NegativeInfinity { get { throw null; } } + public static System.Numerics.Decimal32 NegativeZero { get { throw null; } } + public static System.Numerics.Decimal32 PositiveInfinity { get { throw null; } } + public static System.Numerics.Decimal32 Zero { get { throw null; } } + public int CompareTo(System.Numerics.Decimal32 other) { throw null; } public int CompareTo(object? value) { throw null; } - public int CompareTo(Decimal64 other) { throw null; } - public bool Equals(Decimal64 other) { throw null; } - - public static Decimal64 Parse(string s) { throw null; } - public static Decimal64 Parse(string s, System.Globalization.NumberStyles style) { throw null; } - public static Decimal64 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } - public static Decimal64 Parse(string s, IFormatProvider? provider) { throw null; } - public static Decimal64 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, IFormatProvider? provider = null) { throw null; } - public static Decimal64 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; } - - public static Decimal64 NaN { get { throw null; } } - public static Decimal64 NegativeInfinity { get { throw null; } } - public static Decimal64 PositiveInfinity { get { throw null; } } - public static Decimal64 NegativeZero { get { throw null; } } - public static Decimal64 Zero { get { throw null; } } - public static Decimal64 MaxValue { get { throw null; } } - public static Decimal64 MinValue { get { throw null; } } - - public static Decimal64 Epsilon { get { 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 override int GetHashCode() { 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; } + public static System.Numerics.Decimal32 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 bool operator >=(System.Numerics.Decimal32 left, System.Numerics.Decimal32 right) { throw null; } + public static System.Numerics.Decimal32 operator ++(System.Numerics.Decimal32 value) { 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 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 value) { throw null; } + public static System.Numerics.Decimal32 operator +(System.Numerics.Decimal32 value) { throw null; } + public static System.Numerics.Decimal32 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.Decimal32 Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal32 Parse(string s) { throw null; } + 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 override string ToString() { throw null; } public string ToString(System.IFormatProvider? provider) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format, System.IFormatProvider? provider) { throw null; } - - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal64 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, out Decimal64 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal64 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int bytesConsumed) { 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 bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal32 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal32 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal32 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, out System.Numerics.Decimal32 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal32 result) { throw null; } + public static bool TryParse([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 static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal32 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Numerics.Decimal32 result) { throw null; } } - public readonly struct Decimal128 - : System.IComparable, - System.IComparable, - System.IEquatable, - System.ISpanParsable, - System.Numerics.IMinMaxValue + public readonly partial struct Decimal64 : System.IComparable, System.IComparable, System.IEquatable, System.IParsable, System.ISpanParsable, System.Numerics.IMinMaxValue { + private readonly int _dummyPrimitive; + public static System.Numerics.Decimal64 Epsilon { get { throw null; } } + public static System.Numerics.Decimal64 MaxValue { get { throw null; } } + public static System.Numerics.Decimal64 MinValue { get { throw null; } } + public static System.Numerics.Decimal64 NaN { get { throw null; } } + public static System.Numerics.Decimal64 NegativeInfinity { get { throw null; } } + public static System.Numerics.Decimal64 NegativeZero { get { throw null; } } + public static System.Numerics.Decimal64 PositiveInfinity { get { throw null; } } + public static System.Numerics.Decimal64 Zero { get { throw null; } } + public int CompareTo(System.Numerics.Decimal64 other) { throw null; } public int CompareTo(object? value) { throw null; } - public int CompareTo(Decimal128 other) { throw null; } - public bool Equals(Decimal128 other) { throw null; } - - public static Decimal128 Parse(string s) { throw null; } - public static Decimal128 Parse(string s, System.Globalization.NumberStyles style) { throw null; } - public static Decimal128 Parse(ReadOnlySpan s, IFormatProvider? provider) { throw null; } - public static Decimal128 Parse(string s, IFormatProvider? provider) { throw null; } - public static Decimal128 Parse(ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, IFormatProvider? provider = null) { throw null; } - public static Decimal128 Parse(string s, System.Globalization.NumberStyles style, IFormatProvider? provider) { throw null; } - - public static Decimal128 NaN { get { throw null; } } - public static Decimal128 NegativeInfinity { get { throw null; } } - public static Decimal128 PositiveInfinity { get { throw null; } } - public static Decimal128 NegativeZero { get { throw null; } } - public static Decimal128 Zero { get { throw null; } } - public static Decimal128 MaxValue { get { throw null; } } - public static Decimal128 MinValue { get { throw null; } } - - public static Decimal128 Epsilon { get { 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 override int GetHashCode() { 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; } + public static System.Numerics.Decimal64 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 bool operator >=(System.Numerics.Decimal64 left, System.Numerics.Decimal64 right) { throw null; } + public static System.Numerics.Decimal64 operator ++(System.Numerics.Decimal64 value) { 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 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 value) { throw null; } + public static System.Numerics.Decimal64 operator +(System.Numerics.Decimal64 value) { throw null; } + public static System.Numerics.Decimal64 Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowLeadingWhite | System.Globalization.NumberStyles.AllowThousands | System.Globalization.NumberStyles.AllowTrailingWhite, System.IFormatProvider? provider = null) { throw null; } + public static System.Numerics.Decimal64 Parse(System.ReadOnlySpan s, System.IFormatProvider? provider) { throw null; } + public static System.Numerics.Decimal64 Parse(string s) { throw null; } + 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 override string ToString() { throw null; } public string ToString(System.IFormatProvider? provider) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format) { throw null; } - public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.NumericFormat)] string? format, System.IFormatProvider? provider) { throw null; } - - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, out Decimal128 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, out Decimal128 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } - public static bool TryParse(ReadOnlySpan s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out Decimal128 result) { throw null; } - public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int charsConsumed) { throw null; } - public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal128 result, out int bytesConsumed) { 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 bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal64 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal64 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, out System.Numerics.Decimal64 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal64 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Decimal64 result, out int charsConsumed) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out System.Numerics.Decimal64 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Numerics.Decimal64 result) { throw null; } } public readonly partial struct BFloat16 : 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.IBinaryFloatingPointIeee754, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IExponentialFunctions, System.Numerics.IFloatingPoint, System.Numerics.IFloatingPointConstants, System.Numerics.IFloatingPointIeee754, System.Numerics.IHyperbolicFunctions, System.Numerics.IIncrementOperators, System.Numerics.ILogarithmicFunctions, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IPowerFunctions, System.Numerics.IRootFunctions, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.ITrigonometricFunctions, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj index d527ddcca62fce..a1ac2dff4a8e11 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System.Runtime.Tests.csproj @@ -85,6 +85,7 @@ + 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 4cf72cc508fe66..6e41ab22f78caf 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,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; @@ -503,6 +503,961 @@ public static IEnumerable NaN_NonCanonicalEncodings128_TestData() yield return new object[] { Decimal128.NaN, canonical | (((UInt128)1 << 110) - 1) }; } + public static IEnumerable Zero_NonCanonicalEncodings_TestData() + { + // A finite encoding whose significand exceeds MaxSignificand (10^34 - 1) is non-canonical and represents zero. + // For Decimal128 every large-coefficient (G0G1 == 11) finite encoding is non-canonical. + UInt128 positiveZero = new UInt128(0x3040_0000_0000_0000, 0); + UInt128 negativeZero = new UInt128(0xB040_0000_0000_0000, 0); + + yield return new object[] { positiveZero, new UInt128(0x6C10_7FFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF) }; + yield return new object[] { positiveZero, new UInt128(0x6C10_0000_0000_0000, 0x0000_0000_0000_0001) }; + yield return new object[] { negativeZero, new UInt128(0xEC10_7FFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF) }; + yield return new object[] { negativeZero, new UInt128(0xEC10_0000_0000_0000, 0x0000_0000_0000_0001) }; + } + + [Theory] + [MemberData(nameof(Zero_NonCanonicalEncodings_TestData))] + public static void Finite_NonCanonicalEncodings_BehaveAsZero(UInt128 canonicalZero, UInt128 encoding) + { + Decimal128 zero = Unsafe.BitCast(canonicalZero); + Decimal128 nc = Unsafe.BitCast(encoding); + Decimal128 one = Unsafe.BitCast(new UInt128(0x3040_0000_0000_0000, 1)); + Decimal128 inf = Decimal128.PositiveInfinity; + + // A non-canonical finite encoding compares, hashes, and equals as zero. + Assert.Equal(0, zero.CompareTo(nc)); + Assert.True(zero == nc); + Assert.Equal(zero.GetHashCode(), nc.GetHashCode()); + + // Arithmetic treats the non-canonical operand identically to canonical zero. + Assert.Equal(Bits(zero + one), Bits(nc + one)); + Assert.Equal(Bits(zero - one), Bits(nc - one)); + Assert.Equal(Bits(one + zero), Bits(one + nc)); + Assert.Equal(Bits(zero * one), Bits(nc * one)); + Assert.Equal(Bits(one * zero), Bits(one * nc)); + Assert.Equal(Bits(zero / one), Bits(nc / one)); + + // Division-by-zero and invalid operations must trigger for non-canonical zero. + Assert.Equal(Bits(one / zero), Bits(one / nc)); // finite / 0 -> Infinity + Assert.Equal(Bits(zero / zero), Bits(nc / nc)); // 0 / 0 -> NaN + Assert.Equal(Bits(inf * zero), Bits(inf * nc)); // Infinity * 0 -> NaN + + static UInt128 Bits(Decimal128 value) => Unsafe.BitCast(value); + } + + public static IEnumerable UnaryNegation_TestData() + { + yield return new object[] { new UInt128(0x3040_0000_0000_0000, 0), new UInt128(0xB040_0000_0000_0000, 0) }; // +0 -> -0 + yield return new object[] { new UInt128(0xB040_0000_0000_0000, 0), new UInt128(0x3040_0000_0000_0000, 0) }; // -0 -> +0 + yield return new object[] { new UInt128(0x7800_0000_0000_0000, 0), new UInt128(0xF800_0000_0000_0000, 0) }; // +Infinity -> -Infinity + yield return new object[] { new UInt128(0xF800_0000_0000_0000, 0), new UInt128(0x7800_0000_0000_0000, 0) }; // -Infinity -> +Infinity + yield return new object[] { new UInt128(0xFC00_0000_0000_0000, 0), new UInt128(0x7C00_0000_0000_0000, 0) }; // NaN -> sign-flipped NaN + yield return new object[] { new UInt128(0x7C00_0000_0000_0000, 0), new UInt128(0xFC00_0000_0000_0000, 0) }; + } + + [Theory] + [MemberData(nameof(UnaryNegation_TestData))] + public static void op_UnaryNegation(UInt128 value, UInt128 expected) + { + Decimal128 result = -Unsafe.BitCast(value); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Theory] + [InlineData("123")] + [InlineData("-123")] + [InlineData("4567.891")] + [InlineData("-4567.891")] + [InlineData("9.999999999999999999999999999999999e6144")] + [InlineData("1e-6176")] + public static void op_UnaryNegation_FiniteRoundTrips(string value) + { + Decimal128 d = Decimal128.Parse(value, CultureInfo.InvariantCulture); + Decimal128 negated = -d; + + UInt128 dBits = Unsafe.BitCast(d); + UInt128 negatedBits = Unsafe.BitCast(negated); + + Assert.Equal(dBits ^ new UInt128(0x8000_0000_0000_0000, 0), negatedBits); // only the sign bit differs + Assert.Equal(dBits, Unsafe.BitCast(-negated)); // double negation is identity + } + + [Theory] + [MemberData(nameof(UnaryNegation_TestData))] + public static void op_UnaryPlus(UInt128 value, UInt128 _) + { + Decimal128 d = Unsafe.BitCast(value); + Assert.Equal(value, Unsafe.BitCast(+d)); + } + public static IEnumerable op_Addition_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN + 1 -> NaN + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // 1 + NaN -> NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf + 1 -> +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // 1 + +Inf -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf + -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -Inf + +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf + +Inf -> +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf + -Inf -> -Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 + +0 -> +0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000) }; // -0 + -0 -> -0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 + -0 -> +0 (round-half-even) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // -0 + +0 -> +0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 1 + 0 -> 1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 0 + 1 -> 1 + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf + 1 -> canonical +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000005), new UInt128(0xF800000000000000, 0x0000000000000000) }; // 1 + non-canonical -Inf -> canonical -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x000000000000000F), new UInt128(0xF800000000000000, 0x0000000000000003), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // non-canonical +Inf + non-canonical -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000003) }; // 1 + 2 -> 3 + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000F), new UInt128(0x303E000000000000, 0x0000000000000019), new UInt128(0x303E000000000000, 0x0000000000000028) }; // 1.5 + 2.5 -> 4.0 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000002), new UInt128(0x303E000000000000, 0x0000000000000003) }; // 0.1 + 0.2 -> 0.3 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000) }; // 1 + -1 -> +0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000) }; // -1 + 1 -> +0 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3042314DC6448D93, 0x38C15B0A00000000) }; // all-nines + 1 (carry/overflow to next magnitude) + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3042629B8C891B26, 0x7182B61400000000) }; // big + big (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x2FFC000000000000, 0x0000000000000001), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; // 1 + 1e-P (alignment beyond precision) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x2FF2000000000000, 0x0000000000000001), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; // 1 + tiny (sticky rounding) + yield return new object[] { new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE629B8C891B26, 0x7182B61400000000) }; // max-ish + max-ish (overflow to Inf) + yield return new object[] { new UInt128(0x3034000000000000, 0x000000000012D687), new UInt128(0x3034000000000000, 0x000000000074CBB1), new UInt128(0x3034000000000000, 0x000000000087A238) }; // cohort / preferred exponent + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000064), new UInt128(0x303A000000000000, 0x0000000000000001), new UInt128(0x303A000000000000, 0x00000000000186A1) }; // 100 + 0.001 (exponent spread) + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0xB041ED09BEAD87C0, 0x378D8E63FFFFFFFE), new UInt128(0x3040000000000000, 0x0000000000000001) }; // cancellation leaving small + yield return new object[] { new UInt128(0x300C000000000000, 0x0386DF690C6EF5BE), new UInt128(0xB018000000000000, 0x00022FCFF31AA0B9), new UInt128(0xB00C000000000021, 0x5A87EFD0027FEA82) }; + yield return new object[] { new UInt128(0x3016000000000000, 0x00000000025C222D), new UInt128(0xAFEE0000105989B6, 0x14E871DF7FB4E7FF), new UInt128(0xAFEE0000038E860F, 0x754522FCF5E4E7FF) }; + yield return new object[] { new UInt128(0xAFE4004BC3F60664, 0xA2D28316972FB16C), new UInt128(0x3024000000000000, 0x336683344D857468), new UInt128(0x3006B69C6D6AD20B, 0x43BBF7251F590CE0) }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000000000000009), new UInt128(0xD36C269369438B15, 0x4317B7B7F941F96B), new UInt128(0xD36B81C21CA36ED4, 0x9EED2D2FBC93BE2E) }; + yield return new object[] { new UInt128(0xB030000000000000, 0x00000006C1105459), new UInt128(0xB01E100AA3FBDB37, 0x590C5A8BE72C6FA0), new UInt128(0xB01E100AA3FBDB38, 0xEBA096260A44A9A0) }; + yield return new object[] { new UInt128(0x4592000000000000, 0x0125EB92A4B6C61E), new UInt128(0xB03E000000000000, 0x00000001A8B80287), new UInt128(0x457197E56F0AC19C, 0x7790248FB22C0000) }; + yield return new object[] { new UInt128(0xB018000000000000, 0x000016E8FC21185B), new UInt128(0xB052000000000000, 0x00000000EFF4031C), new UInt128(0xB022C67C0F8B01FB, 0xA2D1A6E0AB03AD2F) }; + yield return new object[] { new UInt128(0x3020000000000000, 0x0000009AA0434574), new UInt128(0x3048000000000000, 0x0000000000001ED7), new UInt128(0x302000000000A72E, 0xE1796A54B2B34574) }; + yield return new object[] { new UInt128(0xB058000000000000, 0x00000000000003DF), new UInt128(0x300C000000000000, 0x000179DCB4BE3FF7), new UInt128(0xB01BE899C8FE6687, 0x7271EA097D860D73) }; + yield return new object[] { new UInt128(0x9EE405C739C662F4, 0x5752242E1DB1EC20), new UInt128(0x3004000000000000, 0x0000BD66F041ECD5), new UInt128(0x2FDE66ACD270BB66, 0x6BD8D5D09E080000) }; + yield return new object[] { new UInt128(0xAFE6000119C10D0F, 0xC98968772E7E6EF7), new UInt128(0xB0020000000002DA, 0x7B7ACAB57EE654B5), new UInt128(0xAFEC426FDE55CADB, 0xA69EFD5C6E732E83) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x00000000004E6990), new UInt128(0x3852000000000000, 0x000000000000031D), new UInt128(0x381588F38AEA0C31, 0x8456F6DC80000000) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000000014204609D), new UInt128(0xB020000000000000, 0x000000000000CCF5), new UInt128(0xB008000000000000, 0x00BA6845C8B3EF63) }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB034000000000000, 0x0000000000000110), new UInt128(0xAFF6861B3A0224EC, 0x9A5FD8E800000000) }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), new UInt128(0xB022000BCAF29294, 0x35C8BEB590DFF359) }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), new UInt128(0xB038000000000000, 0x000006AA2A702669) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0xC6E2000000000000, 0x52ABCE0A9DE14E35), new UInt128(0xC6C525B4F0614608, 0xC7F6706318188000) }; + yield return new object[] { new UInt128(0x0870000000000000, 0x000000DD250AAC57), new UInt128(0xB00000000000092C, 0x7754F6DC4C1D7D08), new UInt128(0xAFEAD5977D016087, 0x955141749AFF4000) }; + yield return new object[] { new UInt128(0xB036000000000000, 0x000000130041C7D7), new UInt128(0xB016000000000000, 0x002262E6BEFE8EBF), new UInt128(0xB016000002A30D1F, 0x4AC08D62A1158EBF) }; + yield return new object[] { new UInt128(0x3036000000000000, 0x00000001B6848ADB), new UInt128(0x301E000000000000, 0x00000000000A8089), new UInt128(0x301E00000000018E, 0xD45E584B23DF3089) }; + yield return new object[] { new UInt128(0xC3AE000000000000, 0x04CF4E8A302A8BDC), new UInt128(0xAFFA00000000002E, 0x3420D6865B6A6E4E), new UInt128(0xC38EAAE0CE12AD01, 0x0D9220AFD4DC0000) }; + yield return new object[] { new UInt128(0xD258000000000000, 0x000000000091C9EF), new UInt128(0x300A000000000000, 0x0000167364209915), new UInt128(0xD223D711ABE4492C, 0x8AD6F30498000000) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x000000000439E2E9), new UInt128(0xB014000000000032, 0x708772352DE98CF2), new UInt128(0xB014000000000032, 0x7087719018B9DF62) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000064), new UInt128(0xB03A000000000000, 0x00003B2E17E3E88B), new UInt128(0xB03A000000000000, 0x00003B2E17E56F2B) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00000000023F5ABE), new UInt128(0xB03A000000006CA7, 0xBAF42939DF4E7BA0), new UInt128(0xB03A000000006CA7, 0xBAF42CA7CB24A660) }; + yield return new object[] { new UInt128(0xC6E6000000000000, 0x00000001B8D3E89E), new UInt128(0x301C000000000007, 0x0964D911A0BBD8F4), new UInt128(0xC6B76CA4E932F6AB, 0xE7ED87915E000000) }; + yield return new object[] { new UInt128(0x305E000000000000, 0x000000000000A71E), new UInt128(0x3026000000000000, 0x000000795A3C873F), new UInt128(0x30261517D8F9A995, 0x6CE399493A3C873F) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000005C867C0), new UInt128(0x302E000000000000, 0x0000000000572AE6), new UInt128(0x301A000000000000, 0x00CAF3EBEC4E7040) }; + yield return new object[] { new UInt128(0xB650000000D0224F, 0xFD6F80D1270DDE07), new UInt128(0xB010000000000000, 0x0000021670F50113), new UInt128(0xB6427C0EBBAA80B2, 0x896A25B17B2F1D80) }; + yield return new object[] { new UInt128(0xB02C000000000006, 0x909F1381718DE51E), new UInt128(0xB01E00000000002D, 0xCE88CD810A4F6601), new UInt128(0xB01E000003E9BAA6, 0xE85BD302FF518901) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x00000000000018F2), new UInt128(0x303C000000000000, 0x000000000046CBDD), new UInt128(0x301A00000000623F, 0xE9B2FED12E21E70E) }; + yield return new object[] { new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0x5F2A89880B37B794, 0xE2748F76B8143000) }; + yield return new object[] { new UInt128(0xD95C0000000051B5, 0x62B3BD63621B218F), new UInt128(0xB01E000000000000, 0x0000000000000000), new UInt128(0xD948BE3E155B3E8C, 0x9858E4AB87085C00) }; + yield return new object[] { new UInt128(0xB0280000000006F8, 0x435BE30981A1CB95), new UInt128(0x54FE000000000000, 0x00015E8772D1DBC6), new UInt128(0x54D8BE05AF221D3F, 0x366FDDE421700000) }; + yield return new object[] { new UInt128(0x5C72000037D35F7D, 0xF4E8427727658DEF), new UInt128(0x3042000000000000, 0x0001692E610A8ED8), new UInt128(0x5C68552EE79591D2, 0xE66B107D55B2CF60) }; + yield return new object[] { new UInt128(0x305C000000000000, 0x000000000000FE36), new UInt128(0x3014000000000000, 0x000000D1D108B375), new UInt128(0x302340DBFBE65F62, 0x49AC0F7DC0016004) }; + yield return new object[] { new UInt128(0xB042000000000000, 0x000000000000A932), new UInt128(0x303E000000000000, 0x000002503A034940), new UInt128(0x303E000000000000, 0x0000025039C131B8) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x587FADD919E02966), new UInt128(0x3014000000000000, 0x0721E25C46337978), new UInt128(0x300E00000000001B, 0x83DC8A991F32535A) }; + yield return new object[] { new UInt128(0xAFFA4821D7034A02, 0xB8ACC25DF6F4CF86), new UInt128(0xB044000000000000, 0x00E1766220EE98D3), new UInt128(0xB02338E458FDFD49, 0x227DFD5961604959) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x000000000007C72E), new UInt128(0x1758000000000000, 0x000000041F72D9AA), new UInt128(0x2FECFB527C560A41, 0x002750E0E0000000) }; + yield return new object[] { new UInt128(0x301A0000000061C8, 0xEEFC10C4D3DADBE3), new UInt128(0xB0100003A1125F48, 0x953E3B9231F49265), new UInt128(0xB01000030BDD259F, 0x3627E5CE5E2F5285) }; + yield return new object[] { new UInt128(0x301000004BB9B532, 0xE5CB89F41922BD32), new UInt128(0xDE64000000000000, 0x0000CCBC550D3404), new UInt128(0xDE3E6EFCC8409C2A, 0x0D9E80DD47A00000) }; + yield return new object[] { new UInt128(0xB024000000000000, 0x000000000116C396), new UInt128(0x3028000000005351, 0x729900C58D0BD408), new UInt128(0x3024000000208BD0, 0xC3C44D2B17880F8A) }; + yield return new object[] { new UInt128(0x300200000220753C, 0x8CA178C667F5ED50), new UInt128(0xD382000000000000, 0x0A69C7380DBC5B46), new UInt128(0xD36371F3777B420E, 0x8D9DF2C029C60000) }; + yield return new object[] { new UInt128(0x300C045E788BFD23, 0xFDC576761B96F039), new UInt128(0xB01A000000000000, 0x0AE0ABB2D104776E), new UInt128(0x300C045E78858158, 0xF6A8BBEE1210C539) }; + yield return new object[] { new UInt128(0xCD32000000000000, 0x00000062ADC4C04D), new UInt128(0xB040000000000000, 0xD235D4832BF52B34), new UInt128(0xCD06D0F5E02D285C, 0x39CE6BD79D400000) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x00017F274F195D94), new UInt128(0x2FFA000000000000, 0x022F53F33F61565B), new UInt128(0xB01ACFB53C9B53FE, 0x7F30F461021FFFF0) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x0000000000000046), new UInt128(0x300A00000005E4A6, 0x3DBCC6C875ABB5B6), new UInt128(0xB00A00585A32650B, 0xAF75BA9F4A544A4A) }; + yield return new object[] { new UInt128(0x3066000000000000, 0x0000000000000015), new UInt128(0x3014000000008E7B, 0xA150E858F5A1264C), new UInt128(0x30266789B9F65C81, 0xF732098AA3786387) }; + yield return new object[] { new UInt128(0x21A60000000048E8, 0xDC7FE586D3707752), new UInt128(0xB024000000000023, 0x2E0D31C20845EFD5), new UInt128(0xB00B3FF58FC5004C, 0xC2033C5B68BF2000) }; + yield return new object[] { new UInt128(0xAFF403A1D73F5ACD, 0x96A762C7BAA70840), new UInt128(0x3042000000000000, 0x000000019FF4DE39), new UInt128(0x3013581237E839A6, 0x10386C6F634043B0) }; + yield return new object[] { new UInt128(0x303C000000000000, 0x00113925FCD09CDA), new UInt128(0x3000000000007140, 0x43CE4908B69E66F9), new UInt128(0x3018EF0539CB08CE, 0x80E380BA007D13FC) }; + yield return new object[] { new UInt128(0x3004000000000000, 0x00003C65D7E4774B), new UInt128(0x301825B99EFDAE69, 0x303766A98D4C34B5), new UInt128(0x3017794035E8D01B, 0xE22A029F84FB127A) }; + yield return new object[] { new UInt128(0xB0220000004AE762, 0xF65E345A6F0E69F9), new UInt128(0x301A000000000000, 0x00000000000DC8E9), new UInt128(0xB01A000B6DEE89B7, 0xBFDD0C9222FDC5A7) }; + yield return new object[] { new UInt128(0x3052000000000000, 0x000000000000002A), new UInt128(0x300000001C3D6C1F, 0x26F3E82E5A4692F0), new UInt128(0x3012CF1373ECB904, 0x67A96D19B1D3BCB5) }; + yield return new object[] { new UInt128(0xB048000000000000, 0x0000000000000280), new UInt128(0xAFFC000000000000, 0x0AE997D86F01F581), new UInt128(0xB00B3B8B5B5056E1, 0x6B3BE0524EDF1EC2) }; + yield return new object[] { new UInt128(0xB018000000120CBF, 0x23F4CD9940BD2B9A), new UInt128(0xDFCC005830554042, 0x149E930F0D337664), new UInt128(0xDFC7587CCD030220, 0x8B6E72CB910676A0) }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0112E59C50FC31A5), new UInt128(0x2FEC16CD5779F833, 0xFD5BB8EB3F0C6FD3), new UInt128(0x2FED944C58617AA0, 0x5472BD3EF2FE6FD3) }; + yield return new object[] { new UInt128(0x3D3E000000000000, 0x00000000E4F7F4D6), new UInt128(0xD270000000000000, 0x00000815ABADD29E), new UInt128(0xD247B64511B61A22, 0x8091E2DEA6C00000) }; + yield return new object[] { new UInt128(0x3034000000000000, 0x00181648272743F6), new UInt128(0x569600000002FBFF, 0x4C2693B7C7D3A38C), new UInt128(0x5684B1E1C8F82556, 0xA82AFFAEA5447800) }; + } + + [Theory] + [MemberData(nameof(op_Addition_TestData))] + public static void op_Addition(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(left) + Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Subtraction_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN - 1 -> NaN + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // 1 - NaN -> NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf - 1 -> +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // 1 - +Inf -> -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf - -Inf -> +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf - +Inf -> -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf - +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -Inf - -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 - +0 -> +0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // -0 - -0 -> +0 (round-half-even) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 - -0 -> +0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000) }; // -0 - +0 -> -0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 1 - 0 -> 1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000001) }; // 0 - 1 -> -1 + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf - 1 -> canonical +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000) }; // 1 - non-canonical -Inf -> canonical +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x000000000000000F), new UInt128(0xF800000000000000, 0x0000000000000003), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf - non-canonical -Inf -> canonical +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0xB040000000000000, 0x0000000000000001) }; // 1 - 2 -> -1 + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000F), new UInt128(0x303E000000000000, 0x0000000000000019), new UInt128(0xB03E000000000000, 0x000000000000000A) }; // 1.5 - 2.5 -> -1.0 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000002), new UInt128(0xB03E000000000000, 0x0000000000000001) }; // 0.1 - 0.2 -> -0.1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002) }; // 1 - -1 -> 2 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000002) }; // -1 - 1 -> -2 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFE) }; // all-nines - 1 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3040000000000000, 0x0000000000000000) }; // big - big -> +0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x2FFC000000000000, 0x0000000000000001), new UInt128(0x2FFDED09BEAD87C0, 0x378D8E63FFFFFFFF) }; // 1 - 1e-P -> 0.999... (alignment beyond precision) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x2FF2000000000000, 0x0000000000000001), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; // 1 - tiny -> 1.000... (sticky rounding) + yield return new object[] { new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE000000000000, 0x0000000000000000) }; // max-ish - max-ish -> +0 (preferred exponent retained) + yield return new object[] { new UInt128(0x3034000000000000, 0x000000000012D687), new UInt128(0x3034000000000000, 0x000000000074CBB1), new UInt128(0xB034000000000000, 0x000000000061F52A) }; // cohort / preferred exponent + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000064), new UInt128(0x303A000000000000, 0x0000000000000001), new UInt128(0x303A000000000000, 0x000000000001869F) }; // 100 - 0.001 -> 99.999 (exponent spread) + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0xB041ED09BEAD87C0, 0x378D8E63FFFFFFFE), new UInt128(0x3042629B8C891B26, 0x7182B61400000000) }; // opposite signs subtract as add (magnitudes add, carry/rounding) + yield return new object[] { new UInt128(0x300C000000000000, 0x0386DF690C6EF5BE), new UInt128(0xB018000000000000, 0x00022FCFF31AA0B9), new UInt128(0x300C000000000021, 0x6195AEA21B5DD5FE) }; + yield return new object[] { new UInt128(0x3016000000000000, 0x00000000025C222D), new UInt128(0xAFEE0000105989B6, 0x14E871DF7FB4E7FF), new UInt128(0x2FEE00001D248D5C, 0xB48BC0C20984E7FF) }; + yield return new object[] { new UInt128(0xAFE4004BC3F60664, 0xA2D28316972FB16C), new UInt128(0x3024000000000000, 0x336683344D857468), new UInt128(0xB006B69C6D6AD20B, 0x43BC6455A5EEF320) }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000000000000009), new UInt128(0xD36C269369438B15, 0x4317B7B7F941F96B), new UInt128(0x536B81C21CA36ED4, 0x9EED2D2FBC93BE2E) }; + yield return new object[] { new UInt128(0xB030000000000000, 0x00000006C1105459), new UInt128(0xB01E100AA3FBDB37, 0x590C5A8BE72C6FA0), new UInt128(0x301E100AA3FBDB35, 0xC6781EF1C41435A0) }; + yield return new object[] { new UInt128(0x4592000000000000, 0x0125EB92A4B6C61E), new UInt128(0xB03E000000000000, 0x00000001A8B80287), new UInt128(0x457197E56F0AC19C, 0x7790248FB22C0000) }; + yield return new object[] { new UInt128(0xB018000000000000, 0x000016E8FC21185B), new UInt128(0xB052000000000000, 0x00000000EFF4031C), new UInt128(0x3022C67C0F8B01FB, 0xA2D1A6E08CFC52D1) }; + yield return new object[] { new UInt128(0x3020000000000000, 0x0000009AA0434574), new UInt128(0x3048000000000000, 0x0000000000001ED7), new UInt128(0xB02000000000A72E, 0xE179691F722CBA8C) }; + yield return new object[] { new UInt128(0xB058000000000000, 0x00000000000003DF), new UInt128(0x300C000000000000, 0x000179DCB4BE3FF7), new UInt128(0xB01BE899C8FE6687, 0x7271EA098279F28D) }; + yield return new object[] { new UInt128(0x9EE405C739C662F4, 0x5752242E1DB1EC20), new UInt128(0x3004000000000000, 0x0000BD66F041ECD5), new UInt128(0xAFDE66ACD270BB66, 0x6BD8D5D09E080000) }; + yield return new object[] { new UInt128(0xAFE6000119C10D0F, 0xC98968772E7E6EF7), new UInt128(0xB0020000000002DA, 0x7B7ACAB57EE654B5), new UInt128(0x2FEC426FDDC588C8, 0xACDFBD26F1F0E17D) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x00000000004E6990), new UInt128(0x3852000000000000, 0x000000000000031D), new UInt128(0xB81588F38AEA0C31, 0x8456F6DC80000000) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000000014204609D), new UInt128(0xB020000000000000, 0x000000000000CCF5), new UInt128(0x3008000000000000, 0x00BA68484CBCB09D) }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB034000000000000, 0x0000000000000110), new UInt128(0x2FF6861B3A0224EC, 0x9A5FD8E800000000) }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), new UInt128(0xB022000BCAF294F2, 0x74C8B0A4143C9E27) }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), new UInt128(0x3038000000000000, 0x000006AA2A702717) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0xC6E2000000000000, 0x52ABCE0A9DE14E35), new UInt128(0x46C525B4F0614608, 0xC7F6706318188000) }; + yield return new object[] { new UInt128(0x0870000000000000, 0x000000DD250AAC57), new UInt128(0xB00000000000092C, 0x7754F6DC4C1D7D08), new UInt128(0x2FEAD5977D016087, 0x955141749AFF4000) }; + yield return new object[] { new UInt128(0xB036000000000000, 0x000000130041C7D7), new UInt128(0xB016000000000000, 0x002262E6BEFE8EBF), new UInt128(0xB016000002A30D1F, 0x4A7BC79523187141) }; + yield return new object[] { new UInt128(0x3036000000000000, 0x00000001B6848ADB), new UInt128(0x301E000000000000, 0x00000000000A8089), new UInt128(0x301E00000000018E, 0xD45E584B23CA2F77) }; + yield return new object[] { new UInt128(0xC3AE000000000000, 0x04CF4E8A302A8BDC), new UInt128(0xAFFA00000000002E, 0x3420D6865B6A6E4E), new UInt128(0xC38EAAE0CE12AD01, 0x0D9220AFD4DC0000) }; + yield return new object[] { new UInt128(0xD258000000000000, 0x000000000091C9EF), new UInt128(0x300A000000000000, 0x0000167364209915), new UInt128(0xD223D711ABE4492C, 0x8AD6F30498000000) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x000000000439E2E9), new UInt128(0xB014000000000032, 0x708772352DE98CF2), new UInt128(0x3014000000000032, 0x708772DA43193A82) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000064), new UInt128(0xB03A000000000000, 0x00003B2E17E3E88B), new UInt128(0x303A000000000000, 0x00003B2E17E261EB) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00000000023F5ABE), new UInt128(0xB03A000000006CA7, 0xBAF42939DF4E7BA0), new UInt128(0x303A000000006CA7, 0xBAF425CBF37850E0) }; + yield return new object[] { new UInt128(0xC6E6000000000000, 0x00000001B8D3E89E), new UInt128(0x301C000000000007, 0x0964D911A0BBD8F4), new UInt128(0xC6B76CA4E932F6AB, 0xE7ED87915E000000) }; + yield return new object[] { new UInt128(0x305E000000000000, 0x000000000000A71E), new UInt128(0x3026000000000000, 0x000000795A3C873F), new UInt128(0x30261517D8F9A995, 0x6CE3985685C378C1) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000005C867C0), new UInt128(0x302E000000000000, 0x0000000000572AE6), new UInt128(0xB01A000000000000, 0x00CAF3EBF7DF3FC0) }; + yield return new object[] { new UInt128(0xB650000000D0224F, 0xFD6F80D1270DDE07), new UInt128(0xB010000000000000, 0x0000021670F50113), new UInt128(0xB6427C0EBBAA80B2, 0x896A25B17B2F1D80) }; + yield return new object[] { new UInt128(0xB02C000000000006, 0x909F1381718DE51E), new UInt128(0xB01E00000000002D, 0xCE88CD810A4F6601), new UInt128(0xB01E000003E9BA4B, 0x4B4A3800EAB2BCFF) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x00000000000018F2), new UInt128(0x303C000000000000, 0x000000000046CBDD), new UInt128(0xB01A00000000623F, 0xE9B2FED12E2218F2) }; + yield return new object[] { new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0x5F2A89880B37B794, 0xE2748F76B8143000) }; + yield return new object[] { new UInt128(0xD95C0000000051B5, 0x62B3BD63621B218F), new UInt128(0xB01E000000000000, 0x0000000000000000), new UInt128(0xD948BE3E155B3E8C, 0x9858E4AB87085C00) }; + yield return new object[] { new UInt128(0xB0280000000006F8, 0x435BE30981A1CB95), new UInt128(0x54FE000000000000, 0x00015E8772D1DBC6), new UInt128(0xD4D8BE05AF221D3F, 0x366FDDE421700000) }; + yield return new object[] { new UInt128(0x5C72000037D35F7D, 0xF4E8427727658DEF), new UInt128(0x3042000000000000, 0x0001692E610A8ED8), new UInt128(0x5C68552EE79591D2, 0xE66B107D55B2CF60) }; + yield return new object[] { new UInt128(0x305C000000000000, 0x000000000000FE36), new UInt128(0x3014000000000000, 0x000000D1D108B375), new UInt128(0x302340DBFBE65F62, 0x49AC0F7DBFFE9FFC) }; + yield return new object[] { new UInt128(0xB042000000000000, 0x000000000000A932), new UInt128(0x303E000000000000, 0x000002503A034940), new UInt128(0xB03E000000000000, 0x000002503A4560C8) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x587FADD919E02966), new UInt128(0x3014000000000000, 0x0721E25C46337978), new UInt128(0xB00E00000000001C, 0x34DBE64B52F2A626) }; + yield return new object[] { new UInt128(0xAFFA4821D7034A02, 0xB8ACC25DF6F4CF86), new UInt128(0xB044000000000000, 0x00E1766220EE98D3), new UInt128(0x302338E458FDFD49, 0x227DE2BCB01BB6A7) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x000000000007C72E), new UInt128(0x1758000000000000, 0x000000041F72D9AA), new UInt128(0x2FECFB527C560A41, 0x002750E0E0000000) }; + yield return new object[] { new UInt128(0x301A0000000061C8, 0xEEFC10C4D3DADBE3), new UInt128(0xB0100003A1125F48, 0x953E3B9231F49265), new UInt128(0x30100004364798F1, 0xF454915605B9D245) }; + yield return new object[] { new UInt128(0x301000004BB9B532, 0xE5CB89F41922BD32), new UInt128(0xDE64000000000000, 0x0000CCBC550D3404), new UInt128(0x5E3E6EFCC8409C2A, 0x0D9E80DD47A00000) }; + yield return new object[] { new UInt128(0xB024000000000000, 0x000000000116C396), new UInt128(0x3028000000005351, 0x729900C58D0BD408), new UInt128(0xB024000000208BD0, 0xC3C44D2B19B596B6) }; + yield return new object[] { new UInt128(0x300200000220753C, 0x8CA178C667F5ED50), new UInt128(0xD382000000000000, 0x0A69C7380DBC5B46), new UInt128(0x536371F3777B420E, 0x8D9DF2C029C60000) }; + yield return new object[] { new UInt128(0x300C045E788BFD23, 0xFDC576761B96F039), new UInt128(0xB01A000000000000, 0x0AE0ABB2D104776E), new UInt128(0x300C045E789278EF, 0x04E230FE251D1B39) }; + yield return new object[] { new UInt128(0xCD32000000000000, 0x00000062ADC4C04D), new UInt128(0xB040000000000000, 0xD235D4832BF52B34), new UInt128(0xCD06D0F5E02D285C, 0x39CE6BD79D400000) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x00017F274F195D94), new UInt128(0x2FFA000000000000, 0x022F53F33F61565B), new UInt128(0xB01ACFB53C9B53FE, 0x7F30F46102200010) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x0000000000000046), new UInt128(0x300A00000005E4A6, 0x3DBCC6C875ABB5B6), new UInt128(0xB00A00585A3E2E58, 0x2AEF483035ABB5B6) }; + yield return new object[] { new UInt128(0x3066000000000000, 0x0000000000000015), new UInt128(0x3014000000008E7B, 0xA150E858F5A1264C), new UInt128(0x30266789B9F65C81, 0xF72D419F5C879C79) }; + yield return new object[] { new UInt128(0x21A60000000048E8, 0xDC7FE586D3707752), new UInt128(0xB024000000000023, 0x2E0D31C20845EFD5), new UInt128(0x300B3FF58FC5004C, 0xC2033C5B68BF2000) }; + yield return new object[] { new UInt128(0xAFF403A1D73F5ACD, 0x96A762C7BAA70840), new UInt128(0x3042000000000000, 0x000000019FF4DE39), new UInt128(0xB013581237E839A6, 0x1243E09E4EBFBC50) }; + yield return new object[] { new UInt128(0x303C000000000000, 0x00113925FCD09CDA), new UInt128(0x3000000000007140, 0x43CE4908B69E66F9), new UInt128(0x3018EF0539CB08CE, 0x80E37FC0F5D2EC04) }; + yield return new object[] { new UInt128(0x3004000000000000, 0x00003C65D7E4774B), new UInt128(0x301825B99EFDAE69, 0x303766A98D4C34B5), new UInt128(0xB017794035E8D01B, 0xE22A029F84F90BAA) }; + yield return new object[] { new UInt128(0xB0220000004AE762, 0xF65E345A6F0E69F9), new UInt128(0x301A000000000000, 0x00000000000DC8E9), new UInt128(0xB01A000B6DEE89B7, 0xBFDD0C9223195779) }; + yield return new object[] { new UInt128(0x3052000000000000, 0x000000000000002A), new UInt128(0x300000001C3D6C1F, 0x26F3E82E5A4692F0), new UInt128(0x3012CF1373ECB903, 0x7515293A4E2C434B) }; + yield return new object[] { new UInt128(0xB048000000000000, 0x0000000000000280), new UInt128(0xAFFC000000000000, 0x0AE997D86F01F581), new UInt128(0xB00B3B8B5B5056E1, 0x6B3BE02DB120E13E) }; + yield return new object[] { new UInt128(0xB018000000120CBF, 0x23F4CD9940BD2B9A), new UInt128(0xDFCC005830554042, 0x149E930F0D337664), new UInt128(0x5FC7587CCD030220, 0x8B6E72CB910676A0) }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0112E59C50FC31A5), new UInt128(0x2FEC16CD5779F833, 0xFD5BB8EB3F0C6FD3), new UInt128(0x2FED66B1A96D8A38, 0x59BB4B6874E5902D) }; + yield return new object[] { new UInt128(0x3D3E000000000000, 0x00000000E4F7F4D6), new UInt128(0xD270000000000000, 0x00000815ABADD29E), new UInt128(0x5247B64511B61A22, 0x8091E2DEA6C00000) }; + yield return new object[] { new UInt128(0x3034000000000000, 0x00181648272743F6), new UInt128(0x569600000002FBFF, 0x4C2693B7C7D3A38C), new UInt128(0xD684B1E1C8F82556, 0xA82AFFAEA5447800) }; + } + + [Theory] + [MemberData(nameof(op_Subtraction_TestData))] + public static void op_Subtraction(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(left) - Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Equality_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000), false }; // NaN == NaN -> false + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false }; // NaN == 1 -> false + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), false }; // 1 == NaN -> false + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), false }; // NaN == +Inf -> false + yield return new object[] { new UInt128(0xFE00000000000000, 0x0000000000000000), new UInt128(0xFE00000000000000, 0x0000000000000000), false }; // sNaN == sNaN -> false + yield return new object[] { new UInt128(0xFE00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false }; // sNaN == 1 -> false + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), true }; // +Inf == +Inf -> true + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), true }; // -Inf == -Inf -> true + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), false }; // +Inf == -Inf -> false + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false }; // +Inf == 1 -> false + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000), true }; // non-canonical +Inf == +Inf -> true + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000005), new UInt128(0xF800000000000000, 0x0000000000000000), true }; // non-canonical -Inf == -Inf -> true + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), true }; // +0 == -0 -> true + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), true }; // -0 == +0 -> true + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), true }; // +0 == +0 -> true + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001), true }; // 1 == 1 -> true + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000001), false }; // 1 == -1 -> false + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001), false }; // -1 == 1 -> false + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x000000000000000A), true }; // 1 == 1.0 -> true (cohort) + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000001), true }; // 1.0 == 1 -> true (cohort) + yield return new object[] { new UInt128(0x303C000000000000, 0x0000000000000064), new UInt128(0x303E000000000000, 0x000000000000000A), true }; // 1.00 == 1.0 -> true (cohort) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000064), new UInt128(0x3044000000000000, 0x0000000000000001), true }; // 100 == 1e2 -> true (cohort) + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000001), false }; // 10 == 1 -> false + yield return new object[] { new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), true }; // large == large -> true + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), true }; // all-nines == all-nines -> true + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFE), false }; // all-nines == near -> false + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000001), true }; // 0.1 == 0.1 -> true + yield return new object[] { new UInt128(0xB03E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000001), false }; // -0.1 == 0.1 -> false + yield return new object[] { new UInt128(0x304A000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), true }; // +0 (exp 5) == +0 -> true (zero cohort) + yield return new object[] { new UInt128(0x301A000000000003, 0x9BCF4E2EDCF12EF7), new UInt128(0x3018000000000024, 0x16190DD4A16BD5A6), true }; + yield return new object[] { new UInt128(0xABF20063BD39CCEE, 0x436C8968918D808D), new UInt128(0x303E000000000000, 0x00230AA75BEE8E14), false }; + yield return new object[] { new UInt128(0x300C00002B873DBB, 0xBEBE1FC587469CE0), new UInt128(0x2FF00016B43C9B4F, 0x23150FD733E05182), false }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000076D2FCDB6B3), new UInt128(0x3000000000000000, 0x0003114CF7F46793), false }; + yield return new object[] { new UInt128(0x3030000000000030, 0x6A45B64B7AA1C513), new UInt128(0x302C0000000012E9, 0x833B357BE730FB6C), true }; + yield return new object[] { new UInt128(0x289C000000000000, 0x000000000004EDA8), new UInt128(0x2896000000000000, 0x0000000013405840), true }; + yield return new object[] { new UInt128(0xB008000000000000, 0x0AC06CB3BC9AF0A1), new UInt128(0xB004000000000004, 0x332A7635AC85FEE4), true }; + yield return new object[] { new UInt128(0xD3FA000000000000, 0x0000000000000F1B), new UInt128(0xB02800044597400B, 0x08FB25BCBB359B1B), false }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0157E2AA499D25FA), new UInt128(0xB048000000000000, 0x000000000000003B), false }; + yield return new object[] { new UInt128(0x1A30000000000000, 0x0970216EDBCE4CB6), new UInt128(0x1A2C000000000003, 0xAFCD0F4DDC95F718), true }; + yield return new object[] { new UInt128(0xB032000002F2A152, 0x9B60F2A4EEAF5036), new UInt128(0x305E000000000000, 0x000000000003CF69), false }; + yield return new object[] { new UInt128(0xAFFC000000000000, 0x000119FF4AE64AB6), new UInt128(0xAFF6000000000000, 0x044D8D3C9393D6F0), true }; + yield return new object[] { new UInt128(0xB024000000000000, 0x0000000000383395), new UInt128(0xB01E000000000000, 0x00000000DB897E08), true }; + yield return new object[] { new UInt128(0x3016000000000000, 0x00000000025C222D), new UInt128(0xAFEE0000105989B6, 0x14E871DF7FB4E7FF), false }; + yield return new object[] { new UInt128(0xAFE4004BC3F60664, 0xA2D28316972FB16C), new UInt128(0x3024000000000000, 0x336683344D857468), false }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000000000000009), new UInt128(0xD38C000000000000, 0x0134998469438B15), false }; + yield return new object[] { new UInt128(0xB030000000000000, 0x00000006C1105459), new UInt128(0xB040000000000000, 0x0000000000000272), false }; + yield return new object[] { new UInt128(0x30360000000000BC, 0x438EB99271BCAFC8), new UInt128(0xB040000000000000, 0x00000000DBE2C593), false }; + yield return new object[] { new UInt128(0x9B3E004F2DD1B479, 0xFC21185B344542C4), new UInt128(0xB052000000000000, 0x00000000EFF4031C), false }; + yield return new object[] { new UInt128(0x3020000000000000, 0x0000009AA0434574), new UInt128(0x301C000000000000, 0x00003C669A472150), true }; + yield return new object[] { new UInt128(0x3048000000000000, 0x000000003A904488), new UInt128(0x3046000000000000, 0x0000000249A2AD50), true }; + yield return new object[] { new UInt128(0x303C0000000000E8, 0xFEE00455BE372834), new UInt128(0x3036000000038E23, 0x9B10EEEF07750B20), true }; + yield return new object[] { new UInt128(0xB012000000000000, 0x0000000002BA9121), new UInt128(0xB00C000000000000, 0x0000000AA8C6E8E8), true }; + yield return new object[] { new UInt128(0x3004000000000000, 0x0000BD66F041ECD5), new UInt128(0x3002000000000000, 0x0007660562934052), true }; + yield return new object[] { new UInt128(0xADE0000000000000, 0x0002A69155BE30BC), new UInt128(0xADDE000000000000, 0x001A81AD596DE758), true }; + yield return new object[] { new UInt128(0xDF96000000000000, 0x000000DF56F88A23), new UInt128(0xDF90000000000000, 0x0003686BBADB98B8), true }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000000000034), new UInt128(0xB016000000000000, 0x0000000000001450), true }; + yield return new object[] { new UInt128(0x334C0000001FE2B8, 0xADA8450705F0175D), new UInt128(0x334800000C749023, 0xD5BAF6BE51C92054), true }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB044000000000000, 0x0766DC043F42224C), false }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), false }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), false }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0x004A00000000001C, 0x8D893304C576A4DE), false }; + yield return new object[] { new UInt128(0xB030000000000000, 0x0000000002CEAE78), new UInt128(0xB02A000000000000, 0x0000000AF75984C0), true }; + yield return new object[] { new UInt128(0xB004000000000000, 0x0000000FAFB5B374), new UInt128(0xDF860003F338CEEF, 0xDDFFE3EAF7A5A1FD), false }; + yield return new object[] { new UInt128(0xB026000000000000, 0x00009E4E44EAB8B0), new UInt128(0x9244000000000000, 0x000004781F6323F3), false }; + yield return new object[] { new UInt128(0xAFFA00000000002E, 0x3420D6865B6A6E4E), new UInt128(0xD258000000000000, 0x000000000091C9EF), false }; + yield return new object[] { new UInt128(0x300A000000000000, 0x0000167364209915), new UInt128(0x301C000000000000, 0x000000000439E2E9), false }; + yield return new object[] { new UInt128(0xB014000000000032, 0x708772352DE98CF2), new UInt128(0xB0120000000001F8, 0x654A7613CB1F8174), true }; + yield return new object[] { new UInt128(0xA7B80000000002FC, 0x39DED1B856E65FE1), new UInt128(0xB0240000000047EB, 0x1D33A9AEB44C6F89), false }; + yield return new object[] { new UInt128(0xB03A000000006CA7, 0xBAF42939DF4E7BA0), new UInt128(0x3012000000008D6C, 0x822E872520B2A4C8), false }; + yield return new object[] { new UInt128(0x1530000000000000, 0x0000000005217896), new UInt128(0x152C000000000000, 0x0000000201131A98), true }; + yield return new object[] { new UInt128(0xB042000000000000, 0x00000000002D231B), new UInt128(0xB040000000000000, 0x0000000001C35F0E), true }; + yield return new object[] { new UInt128(0x300800000041C9DA, 0x0AE55CDA1CDDE16F), new UInt128(0x300600000291E284, 0x6CF5A08520AACE56), true }; + yield return new object[] { new UInt128(0xB024000000000000, 0x00000000007F4CCA), new UInt128(0xB020000000000000, 0x0000000031B9FEE8), true }; + yield return new object[] { new UInt128(0x3050000000000000, 0x000000001572A68A), new UInt128(0x30040058AA3F10A7, 0x9EFB87DE8539A6CD), false }; + yield return new object[] { new UInt128(0xB006000000000000, 0x0000041BDB3C92D4), new UInt128(0xB004000000000000, 0x00002916905DBC48), true }; + yield return new object[] { new UInt128(0x3022000000000002, 0x18ABFEB8FC5D88D3), new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), false }; + yield return new object[] { new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0xB032000000000000, 0x00000000007A59AE), true }; + yield return new object[] { new UInt128(0xB0040315621B218F, 0xE4D69AF338BACC8F), new UInt128(0xB0021ED5D50F4F9E, 0xF0620D80374BFD96), true }; + yield return new object[] { new UInt128(0xB02E000000000000, 0x000000440D74407B), new UInt128(0xB02A000000000000, 0x00001A954169300C), true }; + yield return new object[] { new UInt128(0x302600393862E7E8, 0x73749E8BE815E588), new UInt128(0x3020DF844259E402, 0xFF8B528295889B40), true }; + yield return new object[] { new UInt128(0x3038000000000000, 0x096997BACFE09F53), new UInt128(0x3032000000000024, 0xC478B1BC056E5C38), true }; + yield return new object[] { new UInt128(0x3040000000000000, 0x00000000F4E84277), new UInt128(0x302E000000029939, 0x82A8BC0D5A4BB09F), false }; + yield return new object[] { new UInt128(0x305C000000000000, 0x000000000000FE36), new UInt128(0x3058000000000000, 0x0000000000634D18), true }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x000000008DD9B33D), new UInt128(0xAFF80000004473DF, 0xDFFE9B3EC48B6A65), false }; + yield return new object[] { new UInt128(0xB012000000000000, 0x000000013AA4FC9F), new UInt128(0x3014000000000000, 0x00003245B752F717), false }; + yield return new object[] { new UInt128(0x3002000000000000, 0x081624F7721E25C3), new UInt128(0x2FFE000000000003, 0x28A670A893C6C02C), true }; + yield return new object[] { new UInt128(0xDDC8000000000000, 0x00855569DF51C986), new UInt128(0x3024000000000000, 0x000000000007C72E), false }; + yield return new object[] { new UInt128(0x1758000000000000, 0x000000041F72D9AA), new UInt128(0x301A0000000061C8, 0xEEFC10C4D3DADBE3), false }; + yield return new object[] { new UInt128(0xB0100003A1125F48, 0x953E3B9231F49265), new UInt128(0xB00C016AEB2D385A, 0x4C4F451B83892F74), true }; + } + + [Theory] + [MemberData(nameof(op_Equality_TestData))] + public static void op_Equality(UInt128 left, UInt128 right, bool expected) + { + Decimal128 l = Unsafe.BitCast(left); + Decimal128 r = Unsafe.BitCast(right); + Assert.Equal(expected, l == r); + Assert.Equal(!expected, l != r); + } + + public static IEnumerable op_Comparison_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false, false, false, false }; // NaN vs 1 -> unordered + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), false, false, false, false }; // 1 vs NaN -> unordered + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000), false, false, false, false }; // NaN vs NaN -> unordered + yield return new object[] { new UInt128(0xFE00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false, false, false, false }; // sNaN vs 1 -> unordered + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), false, false, false, false }; // NaN vs +Inf -> unordered + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), false, true, false, true }; // +Inf vs 1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000), true, false, true, false }; // 1 vs +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), true, false, true, false }; // -Inf vs 1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000000), false, true, false, true }; // 1 vs -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), true, false, true, false }; // -Inf vs +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), false, false, true, true }; // +Inf vs +Inf (equal) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), false, false, true, true }; // -Inf vs -Inf (equal) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000), false, false, true, true }; // non-canonical +Inf vs +Inf (equal) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), false, false, true, true }; // +0 vs -0 (equal) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), false, false, true, true }; // -0 vs +0 (equal) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), true, false, true, false }; // 0 vs 1 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000), true, false, true, false }; // -1 vs 0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002), true, false, true, false }; // 1 vs 2 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001), false, true, false, true }; // 2 vs 1 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000001), true, false, true, false }; // -1 vs 1 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000001), false, true, false, true }; // 1 vs -1 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000002), false, true, false, true }; // -1 vs -2 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x000000000000000A), false, false, true, true }; // 1 vs 1.0 (cohort equal) + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000001), false, false, true, true }; // 1.0 vs 1 (cohort equal) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000064), new UInt128(0x3044000000000000, 0x0000000000000001), false, false, true, true }; // 100 vs 1e2 (cohort equal) + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000001), false, true, false, true }; // 10 vs 1 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000002), true, false, true, false }; // 0.1 vs 0.2 + yield return new object[] { new UInt128(0x3034000000000000, 0x000000000098967F), new UInt128(0x3034000000000000, 0x000000000098967E), false, true, false, true }; // close values + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFE), false, true, false, true }; // all-nines vs near + yield return new object[] { new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE04EE2D6D415B, 0x85ACEF8100000000), false, true, false, true }; // large magnitudes + yield return new object[] { new UInt128(0xDFFE314DC6448D93, 0x38C15B0A00000000), new UInt128(0x5FFE314DC6448D93, 0x38C15B0A00000000), true, false, true, false }; // -large vs +large + yield return new object[] { new UInt128(0x2FFE289FF22ABDCC, 0xB2563F2BA4A187B8), new UInt128(0x3032000000002117, 0xA8AB1AC92768DFCC), true, false, true, false }; + yield return new object[] { new UInt128(0xB02A000000000000, 0x0000000003E03EDC), new UInt128(0x3034000000000000, 0x5A65FF2A16E631E0), true, false, true, false }; + yield return new object[] { new UInt128(0xB032000000000A9F, 0x4028DF83A3FBDB37), new UInt128(0x3054000000000000, 0x000000002DF6D94F), true, false, true, false }; + yield return new object[] { new UInt128(0xB03E000000000000, 0x00000001A8B80287), new UInt128(0xB03C000000000000, 0x0000001097301946), false, false, true, true }; + yield return new object[] { new UInt128(0x305C000000000000, 0x00000000003673BF), new UInt128(0x3028000000000000, 0x00000007C09C1527), false, true, false, true }; + yield return new object[] { new UInt128(0xAFFE00000000019B, 0x341186BF3F1DA827), new UInt128(0xB03E000000000000, 0x0005DCB5F7ED6B34), false, true, false, true }; + yield return new object[] { new UInt128(0x300C000000000000, 0x000179DCB4BE3FF7), new UInt128(0xB012000000000000, 0x0000000002BA9121), false, true, false, true }; + yield return new object[] { new UInt128(0x0D04000000000000, 0x001C8C6C22C81050), new UInt128(0x304E000000000000, 0x0000000000001216), true, false, true, false }; + yield return new object[] { new UInt128(0xB008000000000000, 0x0000B26F886C7769), new UInt128(0xB01A000000000000, 0x0000000000000034), true, false, true, false }; + yield return new object[] { new UInt128(0x3036000000000000, 0x0000042026A018FC), new UInt128(0xB020000000000000, 0x000000000000CCF5), false, true, false, true }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB044000000000000, 0x0766DC043F42224C), false, true, false, true }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), true, false, true, false }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), false, true, false, true }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0x004A00000000001C, 0x8D893304C576A4DE), false, true, false, true }; + yield return new object[] { new UInt128(0xB030000000000000, 0x0000000002CEAE78), new UInt128(0x2FEA538FE26D7D1B, 0xD726661AA55DF804), true, false, true, false }; + yield return new object[] { new UInt128(0xDF8E0000001FA271, 0xF338CEEFDDFFE3EA), new UInt128(0xB040000000000000, 0x0B6848AD2793AAE4), true, false, true, false }; + yield return new object[] { new UInt128(0x9244000000000000, 0x000004781F6323F3), new UInt128(0x30220000000009CA, 0xBA5D608B3420D686), true, false, true, false }; + yield return new object[] { new UInt128(0x3034000000000000, 0x000000A44C289F0F), new UInt128(0xB01C000000000000, 0x0000000000023EFC), false, true, false, true }; + yield return new object[] { new UInt128(0xB02600000FD5551A, 0x94F228ED26B2F66C), new UInt128(0xB020003DD9546FD5, 0xD1EFDE5F2B1295E0), false, false, true, true }; + yield return new object[] { new UInt128(0x9D10000000000000, 0x7E51E27048F9F398), new UInt128(0xB04C000000000000, 0x000000000000ECB9), false, true, false, true }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00000000023F5ABE), new UInt128(0xB0280000082CA932, 0x5E4B1837B8D3E89E), false, true, false, true }; + yield return new object[] { new UInt128(0x301C000000000007, 0x0964D911A0BBD8F4), new UInt128(0x305E000000000000, 0x000000000000A71E), true, false, true, false }; + yield return new object[] { new UInt128(0x3026000000000000, 0x000000795A3C873F), new UInt128(0x300800000041C9DA, 0x0AE55CDA1CDDE16F), false, true, false, true }; + yield return new object[] { new UInt128(0xB650000000D0224F, 0xFD6F80D1270DDE07), new UInt128(0x301800007AEF1309, 0xE6818A3F216A34FF), true, false, true, false }; + yield return new object[] { new UInt128(0x3006000A9EFB87DE, 0x8539A6CD7BF62403), new UInt128(0xB006000000000000, 0x0000041BDB3C92D4), false, true, false, true }; + yield return new object[] { new UInt128(0x301C000000000315, 0xFC5D88D34D795D12), new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), true, false, true, false }; + yield return new object[] { new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0xB02E000000000000, 0x00001C5D7E76C0D0), false, true, false, true }; + yield return new object[] { new UInt128(0x3028000000000000, 0x00000000155EEEE3), new UInt128(0xC63C000000000000, 0x0000000000000016), false, true, false, true }; + yield return new object[] { new UInt128(0x3010125D89F7C323, 0x37C02ED0435BE309), new UInt128(0xB012000000000000, 0x000000000009536A), false, true, false, true }; + yield return new object[] { new UInt128(0x3038000000000000, 0x096997BACFE09F53), new UInt128(0x3040000000000000, 0x00000000F4E84277), false, true, false, true }; + yield return new object[] { new UInt128(0x3042000000000000, 0x0001692E610A8ED8), new UInt128(0x303E000000000000, 0x008D161DE81FCC60), false, false, true, true }; + yield return new object[] { new UInt128(0x2A96000000000000, 0x0000000000398706), new UInt128(0xB042000000000000, 0x000000000000A932), false, true, false, true }; + yield return new object[] { new UInt128(0x303E000000000000, 0x000002503A034940), new UInt128(0xB026000000000000, 0x0000000000587FAD), false, true, false, true }; + yield return new object[] { new UInt128(0x3014000000000000, 0x0721E25C46337978), new UInt128(0xAFFA4821D7034A02, 0xB8ACC25DF6F4CF86), false, true, false, true }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00E1766220EE98D3), new UInt128(0xB042000000000000, 0x08CE9FD54951F83E), false, false, true, true }; + yield return new object[] { new UInt128(0x8840000000000531, 0x049414502C46E143), new UInt128(0xB03C000000000000, 0x00000000005D61A7), false, true, false, true }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000031F76932CF2), new UInt128(0xB00A00000022492E, 0xC74AC5D28F7C2B9E), false, true, false, true }; + yield return new object[] { new UInt128(0x301A000000000000, 0x000000696040784C), new UInt128(0x3014000000000000, 0x00019B9FFBD5E8E0), false, false, true, true }; + yield return new object[] { new UInt128(0xB0040000000022D8, 0x1DA76F3AF6929B87), new UInt128(0x30240000F05E5FAC, 0x7D46A35E53517B13), true, false, true, false }; + yield return new object[] { new UInt128(0x300200000220753C, 0x8CA178C667F5ED50), new UInt128(0xB036000000000000, 0x0000000000010401), false, true, false, true }; + yield return new object[] { new UInt128(0xB050000000000000, 0x00000000001B96F0), new UInt128(0xC4DA000000000000, 0x002332BFAE0ABB23), false, true, false, true }; + yield return new object[] { new UInt128(0xCD32000000000000, 0x00000062ADC4C04D), new UInt128(0xD02A000000000000, 0x00000000000014F6), false, true, false, true }; + yield return new object[] { new UInt128(0xB040000000000000, 0x00017F274F195D94), new UInt128(0x2FFA000000000000, 0x022F53F33F61565B), true, false, true, false }; + yield return new object[] { new UInt128(0xB044000000000000, 0x0000000000000046), new UInt128(0x2FFA000000000000, 0x000ED37C5E4A6F24), true, false, true, false }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x000000148D08DAD9), new UInt128(0x2FFA000000000000, 0x1FBD55F356C051B7), true, false, true, false }; + yield return new object[] { new UInt128(0x304A000000000000, 0x00000003CD7FC505), new UInt128(0x3046000000000000, 0x0000017C45E8F5F4), false, false, true, true }; + yield return new object[] { new UInt128(0x46D4000000000000, 0x0000000014D6A755), new UInt128(0x2FE6000001F28868, 0xEDF78B1F515A1856), false, true, false, true }; + yield return new object[] { new UInt128(0xB02C000956B6687B, 0x36CEC180F9999542), new UInt128(0x2FFC001E242C92DD, 0xC816BBACBE275F77), true, false, true, false }; + yield return new object[] { new UInt128(0x2FEC879CB69E66F9, 0x5CF87F593585DB26), new UInt128(0x40CE000000000000, 0x0000000FC627A4F5), true, false, true, false }; + yield return new object[] { new UInt128(0xB02C000000000000, 0x0020BB76731068C7), new UInt128(0x3020000000007B27, 0xC72B0E98A7E6BDA9), true, false, true, false }; + yield return new object[] { new UInt128(0x823000000000002A, 0xA9473055B2F65315), new UInt128(0x300800363B6D91C5, 0x9ECF8577D4668EFE), true, false, true, false }; + yield return new object[] { new UInt128(0xAFE400013C7FC403, 0xE8DFAB61815BDD62), new UInt128(0xAFFC000000000000, 0x0AE997D86F01F581), false, true, false, true }; + yield return new object[] { new UInt128(0xB018000000120CBF, 0x23F4CD9940BD2B9A), new UInt128(0xDFCC005830554042, 0x149E930F0D337664), false, true, false, true }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0112E59C50FC31A5), new UInt128(0xB0180000000007E1, 0xF8A6349B7A2C9257), false, true, false, true }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0000001427C344A7), new UInt128(0x300C000000000000, 0x000000C98DA0AE86), false, false, true, true }; + yield return new object[] { new UInt128(0xD270000000000000, 0x00000815ABADD29E), new UInt128(0xD26C000000000000, 0x000328770FE645B8), false, false, true, true }; + yield return new object[] { new UInt128(0xAFE8000619369D80, 0xCF00A8D8FFBDB9B9), new UInt128(0xAFE6003CFC222708, 0x16069879FD69413A), false, false, true, true }; + yield return new object[] { new UInt128(0x3052000000000000, 0x0000001410258185), new UInt128(0x3050000000000000, 0x000000C8A1770F32), false, false, true, true }; + yield return new object[] { new UInt128(0x2FF6000000000000, 0x08D41FB605523418), new UInt128(0x304A000000000000, 0x00003055B53A1C43), true, false, true, false }; + yield return new object[] { new UInt128(0xB036000000000000, 0x0000000000000012), new UInt128(0xB00E000000000061, 0xC2DBBF40A99DB992), false, true, false, true }; + } + + [Theory] + [MemberData(nameof(op_Comparison_TestData))] + public static void op_Comparison(UInt128 left, UInt128 right, bool lessThan, bool greaterThan, bool lessThanOrEqual, bool greaterThanOrEqual) + { + Decimal128 l = Unsafe.BitCast(left); + Decimal128 r = Unsafe.BitCast(right); + Assert.Equal(lessThan, l < r); + Assert.Equal(greaterThan, l > r); + Assert.Equal(lessThanOrEqual, l <= r); + Assert.Equal(greaterThanOrEqual, l >= r); + } + + public static IEnumerable op_Multiply_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN * 1 -> NaN + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // 1 * NaN -> NaN + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN * +Inf -> NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +0 * +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf * -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf * 1 -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000000) }; // +Inf * -1 -> -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf * 2 -> -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000) }; // -Inf * -2 -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf * +Inf -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // +Inf * -Inf -> -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // -Inf * -Inf -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf * 1 -> canonical +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000005), new UInt128(0xF800000000000000, 0x0000000000000000) }; // 1 * non-canonical -Inf -> canonical -Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 * +0 -> +0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000) }; // -0 * -0 -> +0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000) }; // +0 * -0 -> -0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000) }; // 1 * -0 -> -0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000) }; // -1 * +0 -> -0 + yield return new object[] { new UInt128(0x304A000000000000, 0x0000000000000000), new UInt128(0x3046000000000000, 0x0000000000000000), new UInt128(0x3050000000000000, 0x0000000000000000) }; // 0e5 * 0e3 -> 0e8 (preferred exp) + yield return new object[] { new UInt128(0x3036000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x000000000000000C), new UInt128(0x3036000000000000, 0x0000000000000000) }; // 0e-5 * 12 -> 0e-5 (preferred exp) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x000000000000000A) }; // 2 * 5 -> 10 (exp 0, trailing zero retained) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x303E000000000000, 0x0000000000000005), new UInt128(0x303E000000000000, 0x000000000000000A) }; // 2 * 0.5 -> 1.0 + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000F), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x303E000000000000, 0x000000000000001E) }; // 1.5 * 2 -> 3.0 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303C000000000000, 0x0000000000000001) }; // 0.1 * 0.1 -> 0.01 + yield return new object[] { new UInt128(0x303E000000000000, 0x000000000000000C), new UInt128(0x303E000000000000, 0x000000000000000C), new UInt128(0x303C000000000000, 0x0000000000000090) }; // 1.2 * 1.2 -> 1.44 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000003), new UInt128(0x3040000000000000, 0x0000000000000007), new UInt128(0xB040000000000000, 0x0000000000000015) }; // -3 * 7 -> -21 + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000C), new UInt128(0x3040000000000000, 0x000000000000000C), new UInt128(0x3040000000000000, 0x0000000000000090) }; // 12 * 12 -> 144 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3085ED09BEAD87C0, 0x378D8E63FFFFFFFE) }; // all-nines squared (round) + yield return new object[] { new UInt128(0x2FFE41BD085B676E, 0xF657240D55555555), new UInt128(0x2FFE57A6B5CF3493, 0xF31EDABC71C71C71), new UInt128(0x2FFE74DE47BEF0C5, 0x442923A5ED097B41) }; // full-precision * full-precision (round) + yield return new object[] { new UInt128(0x3040314DC6448D93, 0x38C15B0A00000000), new UInt128(0x3040314DC6448D93, 0x38C15B0A00000000), new UInt128(0x3082314DC6448D93, 0x38C15B0A00000000) }; // 10^(P-1) * 10^(P-1) (trailing zeros beyond precision) + yield return new object[] { new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // huge * huge -> +Inf (overflow) + yield return new object[] { new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -huge * huge -> -Inf (overflow) + yield return new object[] { new UInt128(0x0040000000000000, 0x0000000000000001), new UInt128(0x0040000000000000, 0x0000000000000001), new UInt128(0x0000000000000000, 0x0000000000000000) }; // tiny * tiny -> underflow + yield return new object[] { new UInt128(0x0042000000000000, 0x0000000000000001), new UInt128(0x3036000000000000, 0x0000000000000001), new UInt128(0x0038000000000000, 0x0000000000000001) }; // small * small (subnormal-ish) + yield return new object[] { new UInt128(0x181DED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x181DED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x003DED09BEAD87C0, 0x378D8E63FFFFFFFE) }; // wide product just below min quantum (normal after single rounding) + yield return new object[] { new UInt128(0x17FBED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x17FDED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x0000007E37BE2022, 0xC0914B2680000000) }; // wide product deep in the subnormal range + yield return new object[] { new UInt128(0x0041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x0041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x0000000000000000, 0x0000000000000000) }; // wide product underflow to zero/epsilon + yield return new object[] { new UInt128(0x5FFE000000000000, 0x0000000000000000), new UInt128(0x5FFE000000000000, 0x0000000000000005), new UInt128(0x5FFE000000000000, 0x0000000000000000) }; // zero * finite, preferred exponent far above max quantum (clamped) + yield return new object[] { new UInt128(0xDFFE000000000000, 0x0000000000000000), new UInt128(0x5FFE000000000000, 0x0000000000000005), new UInt128(0xDFFE000000000000, 0x0000000000000000) }; // -zero * finite, preferred exponent clamped (sign preserved) + yield return new object[] { new UInt128(0x3042000000000000, 0x0000000000000000), new UInt128(0x5FFE000000000000, 0x0000000000000005), new UInt128(0x5FFE000000000000, 0x0000000000000000) }; // zero * finite, preferred exponent one above max quantum (clamped) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x5FFE000000000000, 0x0000000000000005), new UInt128(0x5FFE000000000000, 0x0000000000000000) }; // zero * finite, preferred exponent exactly at max quantum (no clamp) + yield return new object[] { new UInt128(0x300C000000000000, 0x0386DF690C6EF5BE), new UInt128(0xB018000000000000, 0x00022FCFF31AA0B9), new UInt128(0xAFE407B65F3E6C11, 0x97E447D9A512564E) }; + yield return new object[] { new UInt128(0x3016000000000000, 0x00000000025C222D), new UInt128(0xAFEE0000105989B6, 0x14E871DF7FB4E7FF), new UInt128(0xAFC862C63C9DC466, 0xEEA20CD21BC0A580) }; + yield return new object[] { new UInt128(0xAFE4004BC3F60664, 0xA2D28316972FB16C), new UInt128(0x3024000000000000, 0x336683344D857468), new UInt128(0xAFE86D9DF48A21EF, 0xA1A8BB1C44333B9B) }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000000000000009), new UInt128(0xD36C269369438B15, 0x4317B7B7F941F96B), new UInt128(0x53535B2EB35FE3BF, 0x5BD57577C351C4C3) }; + yield return new object[] { new UInt128(0xB030000000000000, 0x00000006C1105459), new UInt128(0xB01E100AA3FBDB37, 0x590C5A8BE72C6FA0), new UInt128(0x3021D1590788E3E1, 0xE8D4D9049C75FA8E) }; + yield return new object[] { new UInt128(0x4592000000000000, 0x0125EB92A4B6C61E), new UInt128(0xB03E000000000000, 0x00000001A8B80287), new UInt128(0xC590000001E7A16F, 0x190B7B1E5F7EB5D2) }; + yield return new object[] { new UInt128(0xB018000000000000, 0x000016E8FC21185B), new UInt128(0xB052000000000000, 0x00000000EFF4031C), new UInt128(0x302A000000001579, 0x59BA71B822A3BAF4) }; + yield return new object[] { new UInt128(0x3020000000000000, 0x0000009AA0434574), new UInt128(0x3048000000000000, 0x0000000000001ED7), new UInt128(0x3028000000000000, 0x0012A0A47AA2EC6C) }; + yield return new object[] { new UInt128(0xB058000000000000, 0x00000000000003DF), new UInt128(0x300C000000000000, 0x000179DCB4BE3FF7), new UInt128(0xB024000000000000, 0x05B6BD5FAC799D29) }; + yield return new object[] { new UInt128(0x9EE405C739C662F4, 0x5752242E1DB1EC20), new UInt128(0x3004000000000000, 0x0000BD66F041ECD5), new UInt128(0x9EC27854D8F6C60F, 0x6949AA79C4640D7C) }; + yield return new object[] { new UInt128(0xAFE6000119C10D0F, 0xC98968772E7E6EF7), new UInt128(0xB0020000000002DA, 0x7B7ACAB57EE654B5), new UInt128(0x2FCC39EEA2704D09, 0x18025365B6B0A095) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x00000000004E6990), new UInt128(0x3852000000000000, 0x000000000000031D), new UInt128(0xB820000000000000, 0x00000000F41EA550) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000000014204609D), new UInt128(0xB020000000000000, 0x000000000000CCF5), new UInt128(0xAFE8000000000000, 0x000101CFAB2D9241) }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB034000000000000, 0x0000000000000110), new UInt128(0x0456000000000000, 0x000000F975005D00) }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), new UInt128(0xB0290194CFA11AFA, 0x7F52F9881B76CEAC) }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), new UInt128(0xB034000000000000, 0x000005CC5D3DBB50) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0xC6E2000000000000, 0x52ABCE0A9DE14E35), new UInt128(0xC6DA329B18E3DD29, 0xFE1B6F4B3BC384C8) }; + yield return new object[] { new UInt128(0x0870000000000000, 0x000000DD250AAC57), new UInt128(0xB00000000000092C, 0x7754F6DC4C1D7D08), new UInt128(0x8832CADF15269ABF, 0x15479B9D126F162C) }; + yield return new object[] { new UInt128(0xB036000000000000, 0x000000130041C7D7), new UInt128(0xB016000000000000, 0x002262E6BEFE8EBF), new UInt128(0x300C0000028D5FF6, 0x213D8511873F5B69) }; + yield return new object[] { new UInt128(0x3036000000000000, 0x00000001B6848ADB), new UInt128(0x301E000000000000, 0x00000000000A8089), new UInt128(0x3014000000000000, 0x0011FD5A5EE9CF33) }; + yield return new object[] { new UInt128(0xC3AE000000000000, 0x04CF4E8A302A8BDC), new UInt128(0xAFFA00000000002E, 0x3420D6865B6A6E4E), new UInt128(0x437291A3FAE81214, 0x39CAD6C0814BA89C) }; + yield return new object[] { new UInt128(0xD258000000000000, 0x000000000091C9EF), new UInt128(0x300A000000000000, 0x0000167364209915), new UInt128(0xD22200000000000C, 0xC91145CEC785679B) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x000000000439E2E9), new UInt128(0xB014000000000032, 0x708772352DE98CF2), new UInt128(0xAFF00000D529E131, 0x40A8C42321E0EC42) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000064), new UInt128(0xB03A000000000000, 0x00003B2E17E3E88B), new UInt128(0x303A000000000000, 0x00171E015506D64C) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00000000023F5ABE), new UInt128(0xB03A000000006CA7, 0xBAF42939DF4E7BA0), new UInt128(0x303E00F43340869F, 0x30B182BD001600C0) }; + yield return new object[] { new UInt128(0xC6E6000000000000, 0x00000001B8D3E89E), new UInt128(0x301C000000000007, 0x0964D911A0BBD8F4), new UInt128(0xC6C2000C1DF87810, 0x66C04A42A1A90698) }; + yield return new object[] { new UInt128(0x305E000000000000, 0x000000000000A71E), new UInt128(0x3026000000000000, 0x000000795A3C873F), new UInt128(0x3044000000000000, 0x004F38160F51F262) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000005C867C0), new UInt128(0x302E000000000000, 0x0000000000572AE6), new UInt128(0xB008000000000000, 0x0001F8135552B680) }; + yield return new object[] { new UInt128(0xB650000000D0224F, 0xFD6F80D1270DDE07), new UInt128(0xB010000000000000, 0x0000021670F50113), new UInt128(0x362B1CC33D11D8E8, 0x94F27B03FC307F88) }; + yield return new object[] { new UInt128(0xB02C000000000006, 0x909F1381718DE51E), new UInt128(0xB01E00000000002D, 0xCE88CD810A4F6601), new UInt128(0x301A3273BF0089BE, 0x1BF92234A5C2B6C3) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x00000000000018F2), new UInt128(0x303C000000000000, 0x000000000046CBDD), new UInt128(0xB016000000000000, 0x00000006E6096EEA) }; + yield return new object[] { new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0xDF34000048457EDD, 0x4C4221E35480116A) }; + yield return new object[] { new UInt128(0xD95C0000000051B5, 0x62B3BD63621B218F), new UInt128(0xB01E000000000000, 0x0000000000000000), new UInt128(0x593A000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xB0280000000006F8, 0x435BE30981A1CB95), new UInt128(0x54FE000000000000, 0x00015E8772D1DBC6), new UInt128(0xD4EE3E8B2A46B875, 0x59B96E9577A90909) }; + yield return new object[] { new UInt128(0x5C72000037D35F7D, 0xF4E8427727658DEF), new UInt128(0x3042000000000000, 0x0001692E610A8ED8), new UInt128(0x5C87524835F626B9, 0xBFC37B63EBCC127C) }; + yield return new object[] { new UInt128(0x305C000000000000, 0x000000000000FE36), new UInt128(0x3014000000000000, 0x000000D1D108B375), new UInt128(0x3030000000000000, 0x00D059A8B9E3F0AE) }; + yield return new object[] { new UInt128(0xB042000000000000, 0x000000000000A932), new UInt128(0x303E000000000000, 0x000002503A034940), new UInt128(0xB040000000000000, 0x018769F77FFF8E80) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x587FADD919E02966), new UInt128(0x3014000000000000, 0x0721E25C46337978), new UInt128(0xAFE8A196A59F5076, 0x7DBBCD2DBAAE1214) }; + yield return new object[] { new UInt128(0xAFFA4821D7034A02, 0xB8ACC25DF6F4CF86), new UInt128(0xB044000000000000, 0x00E1766220EE98D3), new UInt128(0x301FC9C3F9A45974, 0xEDCE05153B8B06AB) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x000000000007C72E), new UInt128(0x1758000000000000, 0x000000041F72D9AA), new UInt128(0x173C000000000000, 0x00201153E17C428C) }; + yield return new object[] { new UInt128(0x301A0000000061C8, 0xEEFC10C4D3DADBE3), new UInt128(0xB0100003A1125F48, 0x953E3B9231F49265), new UInt128(0xB0124176B78A0C87, 0x2603353137A081D0) }; + yield return new object[] { new UInt128(0x301000004BB9B532, 0xE5CB89F41922BD32), new UInt128(0xDE64000000000000, 0x0000CCBC550D3404), new UInt128(0xDE47041BDCF88924, 0x3862B211EE51A96E) }; + yield return new object[] { new UInt128(0xB024000000000000, 0x000000000116C396), new UInt128(0x3028000000005351, 0x729900C58D0BD408), new UInt128(0xB00C005ABA1A4E2A, 0x87C41908E82054B0) }; + yield return new object[] { new UInt128(0x300200000220753C, 0x8CA178C667F5ED50), new UInt128(0xD382000000000000, 0x0A69C7380DBC5B46), new UInt128(0xD35AF381555D3933, 0xA140AE1DFC803624) }; + yield return new object[] { new UInt128(0x300C045E788BFD23, 0xFDC576761B96F039), new UInt128(0xB01A000000000000, 0x0AE0ABB2D104776E), new UInt128(0xB0075673706B72C0, 0xB77852E5EF9F5ABF) }; + yield return new object[] { new UInt128(0xCD32000000000000, 0x00000062ADC4C04D), new UInt128(0xB040000000000000, 0xD235D4832BF52B34), new UInt128(0x4D320051074B49F1, 0xC6902C3B50F4FEA4) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x00017F274F195D94), new UInt128(0x2FFA000000000000, 0x022F53F33F61565B), new UInt128(0xAFFA0345247B8835, 0x00326DAB8487FB9C) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x0000000000000046), new UInt128(0x300A00000005E4A6, 0x3DBCC6C875ABB5B6), new UInt128(0xB00E0000019C8574, 0xE19E5AD02CF3AFC4) }; + yield return new object[] { new UInt128(0x3066000000000000, 0x0000000000000015), new UInt128(0x3014000000008E7B, 0xA150E858F5A1264C), new UInt128(0x303A0000000BB024, 0x3BA30F4C2638243C) }; + yield return new object[] { new UInt128(0x21A60000000048E8, 0xDC7FE586D3707752), new UInt128(0xB024000000000023, 0x2E0D31C20845EFD5), new UInt128(0xA1A06E29F26C86CE, 0x9B6E54620B903F9B) }; + yield return new object[] { new UInt128(0xAFF403A1D73F5ACD, 0x96A762C7BAA70840), new UInt128(0x3042000000000000, 0x000000019FF4DE39), new UInt128(0xB006FD79CCA0632A, 0x1CF2022B79252C0A) }; + yield return new object[] { new UInt128(0x303C000000000000, 0x00113925FCD09CDA), new UInt128(0x3000000000007140, 0x43CE4908B69E66F9), new UInt128(0x30087FD4C96325E5, 0x83F8D90398C3CF2C) }; + yield return new object[] { new UInt128(0x3004000000000000, 0x00003C65D7E4774B), new UInt128(0x301825B99EFDAE69, 0x303766A98D4C34B5), new UInt128(0x2FF6FA8670030F05, 0xFFC0D3475619D770) }; + yield return new object[] { new UInt128(0xB0220000004AE762, 0xF65E345A6F0E69F9), new UInt128(0x301A000000000000, 0x00000000000DC8E9), new UInt128(0xAFFC040888F86C54, 0x7D3BF0CDB88DFBA1) }; + yield return new object[] { new UInt128(0x3052000000000000, 0x000000000000002A), new UInt128(0x300000001C3D6C1F, 0x26F3E82E5A4692F0), new UInt128(0x30120004A213BD1C, 0x6404179ACF941B60) }; + yield return new object[] { new UInt128(0xB048000000000000, 0x0000000000000280), new UInt128(0xAFFC000000000000, 0x0AE997D86F01F581), new UInt128(0x300400000000001B, 0x47FB9D1584E5C280) }; + yield return new object[] { new UInt128(0xB018000000120CBF, 0x23F4CD9940BD2B9A), new UInt128(0xDFCC005830554042, 0x149E930F0D337664), new UInt128(0x5FD24B2B8DCF44E4, 0xF9328AD297D2D135) }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0112E59C50FC31A5), new UInt128(0x2FEC16CD5779F833, 0xFD5BB8EB3F0C6FD3), new UInt128(0x2FDAB06F5E049A66, 0x55F91067350E15D9) }; + yield return new object[] { new UInt128(0x3D3E000000000000, 0x00000000E4F7F4D6), new UInt128(0xD270000000000000, 0x00000815ABADD29E), new UInt128(0xDF6E00000000073B, 0x218ADE0A1E7EA814) }; + yield return new object[] { new UInt128(0x3034000000000000, 0x00181648272743F6), new UInt128(0x569600000002FBFF, 0x4C2693B7C7D3A38C), new UInt128(0x5698789A280DEAE7, 0x8FCFA699FD055CA3) }; + } + + [Theory] + [MemberData(nameof(op_Multiply_TestData))] + public static void op_Multiply(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(left) * Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Division_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN / 1 -> NaN + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // 1 / NaN -> NaN + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN / +Inf -> NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf / 1 -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000000) }; // +Inf / -1 -> -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf / 2 -> -Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000) }; // -Inf / -2 -> +Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf / +0 -> +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf / +0 -> -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // +Inf / -0 -> -Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x0000000000000000, 0x0000000000000000) }; // 1 / +Inf -> +0 (Etiny) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x8000000000000000, 0x0000000000000000) }; // -1 / +Inf -> -0 (Etiny) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0x8000000000000000, 0x0000000000000000) }; // 5 / -Inf -> -0 (Etiny) + yield return new object[] { new UInt128(0x3054000000000000, 0x0000000000000005), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x0000000000000000, 0x0000000000000000) }; // 5e10 / +Inf -> +0 (Etiny, dividend exp ignored) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x0000000000000000, 0x0000000000000000) }; // +0 / +Inf -> +0 (Etiny) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x8000000000000000, 0x0000000000000000) }; // -0 / +Inf -> -0 (Etiny) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // 5 / +0 -> +Inf + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -5 / +0 -> -Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // 5 / -0 -> -Inf + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000005), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // -5 / -0 -> +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +0 / +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // -0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x7C00000000000000, 0x0000000000000000) }; // +0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000000) }; // +0 / 5 -> +0 (ideal exp) + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0xB040000000000000, 0x0000000000000000) }; // -0 / 5 -> -0 (ideal exp, sign xor) + yield return new object[] { new UInt128(0x3044000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3044000000000000, 0x0000000000000000) }; // 0e2 / 5 -> 0e2 (ideal exp) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3046000000000000, 0x0000000000000005), new UInt128(0x303A000000000000, 0x0000000000000000) }; // 0 / 5e3 -> 0e-3 (ideal exp) + yield return new object[] { new UInt128(0x304A000000000000, 0x0000000000000000), new UInt128(0x3036000000000000, 0x0000000000000005), new UInt128(0x3054000000000000, 0x0000000000000000) }; // 0e5 / 5e-5 -> 0e10 (ideal exp) + yield return new object[] { new UInt128(0x5FFE000000000000, 0x0000000000000000), new UInt128(0x3036000000000000, 0x0000000000000005), new UInt128(0x5FFE000000000000, 0x0000000000000000) }; // zero dividend, ideal exp far above max quantum (clamped) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x303E000000000000, 0x0000000000000005) }; // 1 / 2 -> 0.5 (exact) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000008), new UInt128(0x303A000000000000, 0x000000000000007D) }; // 1 / 8 -> 0.125 (exact) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000064), new UInt128(0x3040000000000000, 0x0000000000000004), new UInt128(0x3040000000000000, 0x0000000000000019) }; // 100 / 4 -> 25 (exact) + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000005) }; // 10 / 2 -> 5 (exact) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000006), new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x3040000000000000, 0x0000000000000002) }; // 6 / 3 -> 2 (exact) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000005), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 5 / 5 -> 1 (exact) + yield return new object[] { new UInt128(0x303C000000000000, 0x0000000000000064), new UInt128(0x3040000000000000, 0x0000000000000004), new UInt128(0x303C000000000000, 0x0000000000000019) }; // 1.00 / 4 -> 0.25 (exact, ideal exp preserved) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000007), new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x303E000000000000, 0x0000000000000023) }; // 7 / 2 -> 3.5 (exact) + yield return new object[] { new UInt128(0xB040000000000000, 0x000000000000000F), new UInt128(0x3040000000000000, 0x0000000000000004), new UInt128(0xB03C000000000000, 0x0000000000000177) }; // -15 / 4 -> -3.75 (exact, sign) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x2FFCA45894E48295, 0x67D9DA2155555555) }; // 1 / 3 -> repeating (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x2FFD48B129C9052A, 0xCFB3B442AAAAAAAB) }; // 2 / 3 -> repeating (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x00000000000F4240), new UInt128(0x3040000000000000, 0x0000000000000007), new UInt128(0x3008466F1B3D5C89, 0x2C81EFC524924925) }; // 1000000 / 7 (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000003), new UInt128(0x2FFEA45894E48295, 0x67D9DA2155555555) }; // 10 / 3 -> repeating (round) + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3040000000000000, 0x0000000000000007), new UInt128(0x3040466F1B3D5C89, 0x2C81EFC524924924) }; // all-nines / 7 (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x2FBA314DC6448D93, 0x38C15B0A00000000) }; // 1 / all-nines (round) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000007), new UInt128(0x2FFC8CDE367AB912, 0x5903DF8A49249249) }; // 2 / 7 (round) + yield return new object[] { new UInt128(0x2FFE41BD085B676E, 0xF657240D55555555), new UInt128(0x2FFE57A6B5CF3493, 0xF31EDABC71C71C71), new UInt128(0x2FFD71C74F0225D0, 0x29AA2ACB00000001) }; // full-precision / full-precision (round) + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x2FFE36C831A180DC, 0x77F348B5C71C71C7), new UInt128(0x3082000000000000, 0x0000000000000009) }; // all-nines / near-one full precision (round) + yield return new object[] { new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x3036000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // huge / tiny -> +Inf (overflow) + yield return new object[] { new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x3036000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -huge / tiny -> -Inf (overflow) + yield return new object[] { new UInt128(0x0040000000000000, 0x0000000000000001), new UInt128(0x304A000000000000, 0x0000000000000001), new UInt128(0x0036000000000000, 0x0000000000000001) }; // tiny / large -> underflow + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x5FFC000000000000, 0x0000000000000003), new UInt128(0x0040A45894E48295, 0x67D9DA2155555555) }; // 1 / huge -> tiny subnormal (round) + yield return new object[] { new UInt128(0x0042000000000000, 0x0000000000000001), new UInt128(0x304A000000000000, 0x0000000000000001), new UInt128(0x0038000000000000, 0x0000000000000001) }; // small / large (subnormal-ish) + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf / 1 -> canonical +Inf + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0xF800000000000000, 0x0000000000000005), new UInt128(0x8000000000000000, 0x0000000000000000) }; // 1 / non-canonical -Inf -> canonical -0 (Etiny) + yield return new object[] { new UInt128(0x300C000000000000, 0x0386DF690C6EF5BE), new UInt128(0xB018000000000000, 0x00022FCFF31AA0B9), new UInt128(0xAFF6CB90CE781B14, 0x60367827E211020A) }; + yield return new object[] { new UInt128(0x3016000000000000, 0x00000000025C222D), new UInt128(0xAFEE0000105989B6, 0x14E871DF7FB4E7FF), new UInt128(0xAFFD81C848A85A9B, 0x8A95F9D9E2B714E3) }; + yield return new object[] { new UInt128(0xAFE4004BC3F60664, 0xA2D28316972FB16C), new UInt128(0x3024000000000000, 0x336683344D857468), new UInt128(0xAFD64FE830A0E8A9, 0x51A5EFA9F2EE31EA) }; + yield return new object[] { new UInt128(0xB026000000000000, 0x0000000000000009), new UInt128(0xD36C269369438B15, 0x4317B7B7F941F96B), new UInt128(0x0C7838B6B6D1A502, 0x7FE0879DCC18152B) }; + yield return new object[] { new UInt128(0xB030000000000000, 0x00000006C1105459), new UInt128(0xB01E100AA3FBDB37, 0x590C5A8BE72C6FA0), new UInt128(0x2FE3B79655E99EE6, 0xC67B9FCF86368529) }; + yield return new object[] { new UInt128(0x4592000000000000, 0x0125EB92A4B6C61E), new UInt128(0xB03E000000000000, 0x00000001A8B80287), new UInt128(0xC560393E687AE539, 0x7B7E802B3D5289EF) }; + yield return new object[] { new UInt128(0xB018000000000000, 0x000016E8FC21185B), new UInt128(0xB052000000000000, 0x00000000EFF4031C), new UInt128(0x2FCB348106F6193F, 0x6654C91C46250F08) }; + yield return new object[] { new UInt128(0x3020000000000000, 0x0000009AA0434574), new UInt128(0x3048000000000000, 0x0000000000001ED7), new UInt128(0x2FE59EBC2E5756FC, 0x9ACF1030DDA3964B) }; + yield return new object[] { new UInt128(0xB058000000000000, 0x00000000000003DF), new UInt128(0x300C000000000000, 0x000179DCB4BE3FF7), new UInt128(0xB032759A8AB12BE9, 0xCFE4870EFACD0EAF) }; + yield return new object[] { new UInt128(0x9EE405C739C662F4, 0x5752242E1DB1EC20), new UInt128(0x3004000000000000, 0x0000BD66F041ECD5), new UInt128(0x9F0115774EEFB4D0, 0xFE3D466FFB652EAD) }; + yield return new object[] { new UInt128(0xAFE6000119C10D0F, 0xC98968772E7E6EF7), new UInt128(0xB0020000000002DA, 0x7B7ACAB57EE654B5), new UInt128(0x2FEF3F0D3347DCD8, 0x95957D6691C04392) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x00000000004E6990), new UInt128(0x3852000000000000, 0x000000000000031D), new UInt128(0xA7C13DE5A78B5C8A, 0x03E165F3CC9B7686) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000000014204609D), new UInt128(0xB020000000000000, 0x000000000000CCF5), new UInt128(0xAFF032C433C139C8, 0xF02601142C32D705) }; + yield return new object[] { new UInt128(0x8462000000000000, 0x00000000EAC878D0), new UInt128(0xB034000000000000, 0x0000000000000110), new UInt128(0x043A476666F4A173, 0x9A4CE8D8AE5A5A5A) }; + yield return new object[] { new UInt128(0xB02400012DE50EC6, 0xBBBA78C47B74A0E0), new UInt128(0x302200000000012F, 0x1F7FF8F741AE5567), new UInt128(0xB00E5262044C38C4, 0xFEF19903FCA8CB97) }; + yield return new object[] { new UInt128(0x3038000000000000, 0x0000000000000057), new UInt128(0xB03C000000000000, 0x000000110FC8CD30), new UInt128(0xAFE83A891034CE03, 0xC2DCA5C65F32C6C8) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x17E95893CB288412), new UInt128(0xC6E2000000000000, 0x52ABCE0A9DE14E35), new UInt128(0x994A8E9AC9234B50, 0x505AA880878F3D7F) }; + yield return new object[] { new UInt128(0x0870000000000000, 0x000000DD250AAC57), new UInt128(0xB00000000000092C, 0x7754F6DC4C1D7D08), new UInt128(0x88586C18C7716200, 0xD6EFD4695899BC44) }; + yield return new object[] { new UInt128(0xB036000000000000, 0x000000130041C7D7), new UInt128(0xB016000000000000, 0x002262E6BEFE8EBF), new UInt128(0x30139FB5EFDD0FB7, 0xF49B951DA6A4F2B4) }; + yield return new object[] { new UInt128(0x3036000000000000, 0x00000001B6848ADB), new UInt128(0x301E000000000000, 0x00000000000A8089), new UInt128(0x301E34B3DBEFDA00, 0x228A6FF58A1CF2E8) }; + yield return new object[] { new UInt128(0xC3AE000000000000, 0x04CF4E8A302A8BDC), new UInt128(0xAFFA00000000002E, 0x3420D6865B6A6E4E), new UInt128(0x43AAC87D34C14517, 0x02E1E1BF053FE3E4) }; + yield return new object[] { new UInt128(0xD258000000000000, 0x000000000091C9EF), new UInt128(0x300A000000000000, 0x0000167364209915), new UInt128(0xD23EBED54CBE4A75, 0x1E37535387AACE6D) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x000000000439E2E9), new UInt128(0xB014000000000032, 0x708772352DE98CF2), new UInt128(0xAFEB77B55CD376ED, 0x4CAC31E2635348AD) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000064), new UInt128(0xB03A000000000000, 0x00003B2E17E3E88B), new UInt128(0x2FEC4BC57A20CF80, 0xFB85C69A5C979594) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x00000000023F5ABE), new UInt128(0xB03A000000006CA7, 0xBAF42939DF4E7BA0), new UInt128(0x2FE76A508107AB67, 0x9C4270C148A575DA) }; + yield return new object[] { new UInt128(0xC6E6000000000000, 0x00000001B8D3E89E), new UInt128(0x301C000000000007, 0x0964D911A0BBD8F4), new UInt128(0xC6B318EB37D2AA50, 0x5FE753A62F2E7B02) }; + yield return new object[] { new UInt128(0x305E000000000000, 0x000000000000A71E), new UInt128(0x3026000000000000, 0x000000795A3C873F), new UInt128(0x302794B3266BDB84, 0x37128AFACE65D3A5) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000005C867C0), new UInt128(0x302E000000000000, 0x0000000000572AE6), new UInt128(0xAFEC53BC1D980FB6, 0xE421BC3CC6718D7B) }; + yield return new object[] { new UInt128(0xB650000000D0224F, 0xFD6F80D1270DDE07), new UInt128(0xB010000000000000, 0x0000021670F50113), new UInt128(0x365A360BC5384886, 0x1E398FB62F6118F3) }; + yield return new object[] { new UInt128(0xB02C000000000006, 0x909F1381718DE51E), new UInt128(0xB01E00000000002D, 0xCE88CD810A4F6601), new UInt128(0x300A46A940A69240, 0x67EBE25A5A68E21F) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x00000000000018F2), new UInt128(0x303C000000000000, 0x000000000046CBDD), new UInt128(0xAFD643DC59C6E179, 0x77D217EDE86BABCC) }; + yield return new object[] { new UInt128(0x5F400000000005E8, 0x2C8923BCBDB9FD3E), new UInt128(0xB034000000000000, 0x00000000000C3C2B), new UInt128(0xDF2AAB8556757D28, 0xD46648FBAAFF5F2F) }; + yield return new object[] { new UInt128(0xD95C0000000051B5, 0x62B3BD63621B218F), new UInt128(0xB01E000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; + yield return new object[] { new UInt128(0xB0280000000006F8, 0x435BE30981A1CB95), new UInt128(0x54FE000000000000, 0x00015E8772D1DBC6), new UInt128(0x8B37A50D2C37AC59, 0xD606984662943B6A) }; + yield return new object[] { new UInt128(0x5C72000037D35F7D, 0xF4E8427727658DEF), new UInt128(0x3042000000000000, 0x0001692E610A8ED8), new UInt128(0x5C48D6803B794C4E, 0xA6922295D7A11D64) }; + yield return new object[] { new UInt128(0x305C000000000000, 0x000000000000FE36), new UInt128(0x3014000000000000, 0x000000D1D108B375), new UInt128(0x3037640DA7EB4F3D, 0xF70D116B4CC77265) }; + yield return new object[] { new UInt128(0xB042000000000000, 0x000000000000A932), new UInt128(0x303E000000000000, 0x000002503A034940), new UInt128(0xAFF253F531466EE5, 0x20D5831D0B9F13FC) }; + yield return new object[] { new UInt128(0xB00E000000000000, 0x587FADD919E02966), new UInt128(0x3014000000000000, 0x0721E25C46337978), new UInt128(0xAFFA3D2D2C168F46, 0x26985E7DE8022E37) }; + yield return new object[] { new UInt128(0xAFFA4821D7034A02, 0xB8ACC25DF6F4CF86), new UInt128(0xB044000000000000, 0x00E1766220EE98D3), new UInt128(0x2FD471A975C830ED, 0x0211EE4AF299EBED) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x000000000007C72E), new UInt128(0x1758000000000000, 0x000000041F72D9AA), new UInt128(0x48C08DEE0D62CA1A, 0x64D9A54AF884B677) }; + yield return new object[] { new UInt128(0x301A0000000061C8, 0xEEFC10C4D3DADBE3), new UInt128(0xB0100003A1125F48, 0x953E3B9231F49265), new UInt128(0xAFFC4F2E6E9690B3, 0xDCB74C8FE1EF531E) }; + yield return new object[] { new UInt128(0x301000004BB9B532, 0xE5CB89F41922BD32), new UInt128(0xDE64000000000000, 0x0000CCBC550D3404), new UInt128(0x81C6335464D97738, 0xE074EA1AE02D536C) }; + yield return new object[] { new UInt128(0xB024000000000000, 0x000000000116C396), new UInt128(0x3028000000005351, 0x729900C58D0BD408), new UInt128(0xAFD8E4ED692C9CD4, 0xAE2CE29C4965AF22) }; + yield return new object[] { new UInt128(0x300200000220753C, 0x8CA178C667F5ED50), new UInt128(0xD382000000000000, 0x0A69C7380DBC5B46), new UInt128(0x8C8FB07E9B67A8C7, 0x7F8CF2E324F5ED40) }; + yield return new object[] { new UInt128(0x300C045E788BFD23, 0xFDC576761B96F039), new UInt128(0xB01A000000000000, 0x0AE0ABB2D104776E), new UInt128(0xB00C37BD9345F786, 0x7BCC90D8781F904C) }; + yield return new object[] { new UInt128(0xCD32000000000000, 0x00000062ADC4C04D), new UInt128(0xB040000000000000, 0xD235D4832BF52B34), new UInt128(0x4CE089F3E8481FD9, 0x898A4C67B47BC8DA) }; + yield return new object[] { new UInt128(0xB040000000000000, 0x00017F274F195D94), new UInt128(0x2FFA000000000000, 0x022F53F33F61565B), new UInt128(0xB03E83EE559A4D25, 0xE27D334E7E6D35F9) }; + yield return new object[] { new UInt128(0xB044000000000000, 0x0000000000000046), new UInt128(0x300A00000005E4A6, 0x3DBCC6C875ABB5B6), new UInt128(0xB009E46DEC94605E, 0xC7571D5C27FE5D49) }; + yield return new object[] { new UInt128(0x3066000000000000, 0x0000000000000015), new UInt128(0x3014000000008E7B, 0xA150E858F5A1264C), new UInt128(0x302299E0D48E6734, 0x207E4332183038FA) }; + yield return new object[] { new UInt128(0x21A60000000048E8, 0xDC7FE586D3707752), new UInt128(0xB024000000000023, 0x2E0D31C20845EFD5), new UInt128(0xA1850595706B4AF7, 0x28FE4C6B3E8361ED) }; + yield return new object[] { new UInt128(0xAFF403A1D73F5ACD, 0x96A762C7BAA70840), new UInt128(0x3042000000000000, 0x000000019FF4DE39), new UInt128(0xAFDC340C30509846, 0x39B2D63AA1FE08FB) }; + yield return new object[] { new UInt128(0x303C000000000000, 0x00113925FCD09CDA), new UInt128(0x3000000000007140, 0x43CE4908B69E66F9), new UInt128(0x3029BEEC6622872B, 0x7A151FE09435B4FF) }; + yield return new object[] { new UInt128(0x3004000000000000, 0x00003C65D7E4774B), new UInt128(0x301825B99EFDAE69, 0x303766A98D4C34B5), new UInt128(0x2FC3ABE8CF0915E5, 0xDDD46FB7F4B41926) }; + yield return new object[] { new UInt128(0xB0220000004AE762, 0xF65E345A6F0E69F9), new UInt128(0x301A000000000000, 0x00000000000DC8E9), new UInt128(0xB02E316B8C450FA4, 0x5CB5EF3047A81DAF) }; + yield return new object[] { new UInt128(0x3052000000000000, 0x000000000000002A), new UInt128(0x300000001C3D6C1F, 0x26F3E82E5A4692F0), new UInt128(0x301AECEEFA9A5EDB, 0x6AF2737449986B22) }; + yield return new object[] { new UInt128(0xB048000000000000, 0x0000000000000280), new UInt128(0xAFFC000000000000, 0x0AE997D86F01F581), new UInt128(0x302B914A094EF4D1, 0x6321CF5E46D6E827) }; + yield return new object[] { new UInt128(0xB018000000120CBF, 0x23F4CD9940BD2B9A), new UInt128(0xDFCC005830554042, 0x149E930F0D337664), new UInt128(0x003E99FA6C26C43F, 0x6C3B7206FB91392C) }; + yield return new object[] { new UInt128(0x300E000000000000, 0x0112E59C50FC31A5), new UInt128(0x2FEC16CD5779F833, 0xFD5BB8EB3F0C6FD3), new UInt128(0x3000527D27495963, 0xEE1F2C36110345C1) }; + yield return new object[] { new UInt128(0x3D3E000000000000, 0x00000000E4F7F4D6), new UInt128(0xD270000000000000, 0x00000815ABADD29E), new UInt128(0x9AC4D51105256889, 0x3089FD94A8A3BBBF) }; + yield return new object[] { new UInt128(0x3034000000000000, 0x00181648272743F6), new UInt128(0x569600000002FBFF, 0x4C2693B7C7D3A38C), new UInt128(0x098A5CA6C49E66A5, 0x1E6D169ACA7DC27B) }; + } + + [Theory] + [MemberData(nameof(op_Division_TestData))] + public static void op_Division(UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(left) / Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Increment_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf (canonicalizes) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001) }; // +0 -> 1 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001) }; // -0 -> 1 + yield return new object[] { new UInt128(0x304A000000000000, 0x0000000000000000), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { new UInt128(0x3036000000000000, 0x0000000000000000), new UInt128(0x3036000000000000, 0x00000000000186A0) }; // 0e-5 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000002) }; // 1 -> 2 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000) }; // -1 -> 0 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000003) }; // 2 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000002), new UInt128(0xB040000000000000, 0x0000000000000001) }; // -2 + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x000000000000000B) }; // 10 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000005), new UInt128(0x303E000000000000, 0x000000000000000F) }; // 0.5 -> 1.5 + yield return new object[] { new UInt128(0xB03E000000000000, 0x0000000000000005), new UInt128(0x303E000000000000, 0x0000000000000005) }; // -0.5 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000019), new UInt128(0x303E000000000000, 0x0000000000000023) }; // 2.5 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0x303E000000000000, 0x000000000000000B) }; // 0.1 -> 1.1 + yield return new object[] { new UInt128(0x303C000000000000, 0x0000000000000177), new UInt128(0x303C000000000000, 0x00000000000001DB) }; // 3.75 + yield return new object[] { new UInt128(0xB03C000000000000, 0x0000000000000019), new UInt128(0x303C000000000000, 0x000000000000004B) }; // -0.25 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000009), new UInt128(0x3040000000000000, 0x000000000000000A) }; // 9 -> 10 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3042314DC6448D93, 0x38C15B0A00000000) }; // all-nines (overflows precision, rounds) + yield return new object[] { new UInt128(0x3040314DC6448D93, 0x38C15B0A00000000), new UInt128(0x3040314DC6448D93, 0x38C15B0A00000001) }; // 10^(P-1) + yield return new object[] { new UInt128(0x3040314DC6448D93, 0x38C15B09FFFFFFFF), new UInt128(0x3040314DC6448D93, 0x38C15B0A00000000) }; // (P-1)-nines + yield return new object[] { new UInt128(0x3088000000000000, 0x0000000000000001), new UInt128(0x3046314DC6448D93, 0x38C15B0A00000000) }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000) }; // near-max (1 negligible) + yield return new object[] { new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000) }; // near -max (1 negligible) + yield return new object[] { new UInt128(0x0040000000000000, 0x0000000000000001), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; // tiny positive subnormal (++ ~ 1) + yield return new object[] { new UInt128(0x8040000000000000, 0x0000000000000001), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; // tiny negative subnormal (++ ~ 1) + yield return new object[] { new UInt128(0x3036000000000000, 0x0000000000000001), new UInt128(0x3036000000000000, 0x00000000000186A1) }; // 1e-5 + yield return new object[] { new UInt128(0x2FFE41BD085B676E, 0xF657240D55555555), new UInt128(0x2FFE730ACE9FF502, 0x2F187F1755555555) }; // 1.333... full precision + yield return new object[] { new UInt128(0x2FFFED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3000363BF3B1CEEE, 0xBE6E4A8B00000000) }; // 9.999... full precision (carry) + yield return new object[] { new UInt128(0xB0000000000000B1, 0x4C1E4DF5ED261C68), new UInt128(0x300004EE2D6D40AA, 0x398EA18B12D9E398) }; + yield return new object[] { new UInt128(0xB02C000000008117, 0x952633E9760D3996), new UInt128(0xB02C000000008117, 0x952633E722015596) }; + yield return new object[] { new UInt128(0xAFF80000000000BC, 0x57570583228BEDE5), new UInt128(0x2FFDED09BEAD87BE, 0x556649372B2CAE43) }; + yield return new object[] { new UInt128(0x27C800000029E4B4, 0x709EF360CF7CC897), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0x3062000000000000, 0x0000000000000006), new UInt128(0x3040000000000000, 0x0853A0D2313C0001) }; + yield return new object[] { new UInt128(0xB02C000000000000, 0x0000467047DBE843), new UInt128(0xB02C000000000000, 0x0000466DF3D00443) }; + yield return new object[] { new UInt128(0x301204EA7A9BDA00, 0xB6F5E4F86A521036), new UInt128(0x301204EA7A9BEF2D, 0xB9BDC64360D21036) }; + yield return new object[] { new UInt128(0x30020000000085EF, 0xF5A15252168838A9), new UInt128(0x3002007E37BEA612, 0xB6329D78968838A9) }; + yield return new object[] { new UInt128(0x3018000000000000, 0x5DF1A27C94EB2D4F), new UInt128(0x3018000000000005, 0xC9B900A9F7FB2D4F) }; + yield return new object[] { new UInt128(0x304C000000000000, 0x0000000000000009), new UInt128(0x3040000000000000, 0x0000000000895441) }; + yield return new object[] { new UInt128(0xB0180000003AF28B, 0xE740E213A5668D80), new UInt128(0xB0180000003AF286, 0x7B7983E642568D80) }; + yield return new object[] { new UInt128(0xB01E000000000000, 0x0000000616E4354B), new UInt128(0x301E000000000000, 0x0163457246A5CAB5) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x0000000000000005), new UInt128(0x3024000000000000, 0x00005AF3107A4005) }; + yield return new object[] { new UInt128(0xCA2C00012586FEF1, 0xA857CDDF6237FE18), new UInt128(0xCA23BFE31CC37D7E, 0xFA82FB3EB0175F00) }; + yield return new object[] { new UInt128(0xB014000000000000, 0x00000000293EF566), new UInt128(0x301400000000021E, 0x19E0C9BA89010A9A) }; + yield return new object[] { new UInt128(0x81D4000000000000, 0x5D3FA1D19308E202), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB038000000000000, 0x0000000004FDAAAC), new UInt128(0xB038000000000000, 0x0000000004FD839C) }; + yield return new object[] { new UInt128(0xB6D2000000000000, 0x000000000000001D), new UInt128(0xB6928EFB2560675E, 0x2497219D00000000) }; + yield return new object[] { new UInt128(0x3026000101B0C4EA, 0xAB917F63F245E3FF), new UInt128(0x3026000101B0C4EA, 0xAB91887C40B883FF) }; + yield return new object[] { new UInt128(0xB0140000BC4F01DB, 0x04D54985FC8DE0DB), new UInt128(0xB0140000BC4EFFBC, 0xEAF47FCB4A4DE0DB) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000007AFAEE4D58A), new UInt128(0x30080000204FCE5E, 0x3E250A10BEE4D58A) }; + yield return new object[] { new UInt128(0x84840000C3B35E24, 0xD944C9B454C06345), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xD9EE000000000000, 0x000000000000005A), new UInt128(0xD9AFBBBBF868FA2C, 0xFECC335A00000000) }; + yield return new object[] { new UInt128(0x2FF6000000000007, 0x1BF19A20781F7D89), new UInt128(0x2FFE314DC6448D93, 0x38EFF238F422346A) }; + yield return new object[] { new UInt128(0x301C0000086A265B, 0x3BC9B1C066BAF9CB), new UInt128(0x301C0000086A265B, 0x49AA68740E1EF9CB) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x000000002A8CA672), new UInt128(0x3030000000000000, 0x0000000030828772) }; + yield return new object[] { new UInt128(0x2FF229E50E8F6A9F, 0xAE76568536C55686), new UInt128(0x2FFE314DC9036E2B, 0xC0531A6B0824E929) }; + yield return new object[] { new UInt128(0x4BF6000000000000, 0x0000000000015DFE), new UInt128(0x4BBDB9C093345A45, 0x21A2D72AC0000000) }; + yield return new object[] { new UInt128(0x3010000001BB3680, 0x155F5B37B10809BD), new UInt128(0x3010000001BC0A42, 0x312E2825520809BD) }; + yield return new object[] { new UInt128(0xAFF8000000000056, 0xAD502213EE481751), new UInt128(0x2FFDED09BEAD87BF, 0x59A8EA35B87F4772) }; + yield return new object[] { new UInt128(0x2FF20060F3D32F96, 0xC08CDAE35E8F6169), new UInt128(0x2FFE314DC64AE82A, 0xC92FA2EF72CB9C9E) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x0A29F51D6081C988), new UInt128(0x301C000000000000, 0x180AABD107E5C988) }; + yield return new object[] { new UInt128(0xAFFA000000046488, 0x16C345A9670E0BDD), new UInt128(0x2FFDED09BEAD174C, 0x3546D43975B1CB9D) }; + yield return new object[] { new UInt128(0x2CFA000000000000, 0x00000000003305E2), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0x9646000000F92EA4, 0x0C7335403F583DB1), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB02C005606ABA329, 0xA60456ECE8C0B9E7), new UInt128(0xB02C005606ABA329, 0xA60456EA94B4D5E7) }; + yield return new object[] { new UInt128(0xB05E000000000000, 0x0000000000000014), new UInt128(0xB040000000000000, 0x00470DE4DF81FFFF) }; + yield return new object[] { new UInt128(0x300A000000000000, 0x004433E77852218C), new UInt128(0x300A0000033B2E3C, 0xA014B4246052218C) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000000000003), new UInt128(0x301A000000000000, 0x8AC7230489E7FFFD) }; + yield return new object[] { new UInt128(0x303A000000000000, 0x0132602F948B8E0D), new UInt128(0x303A000000000000, 0x0132602F948B91F5) }; + yield return new object[] { new UInt128(0xB00000000016390D, 0xBD0D18E1CAF1E112), new UInt128(0x300004EE2D57084D, 0xC89FD69F350E1EEE) }; + yield return new object[] { new UInt128(0x301A000000000000, 0x00000793A637ABF6), new UInt128(0x301A000000000000, 0x8AC72A98301FABF6) }; + yield return new object[] { new UInt128(0x8EA6000000000000, 0x0000000000000037), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB016000000000175, 0x8A684F948BA6A804), new UInt128(0xB01600000000013F, 0x549EA1CEAD06A804) }; + yield return new object[] { new UInt128(0xB0260000C03688D2, 0x7AC2450F1321F8C8), new UInt128(0xB0260000C03688D2, 0x7AC23BF6C4AF58C8) }; + yield return new object[] { new UInt128(0x5402000000000000, 0x0000000CF771D12A), new UInt128(0x53D51293F938DCC4, 0xF7A5780AF1000000) }; + yield return new object[] { new UInt128(0xB00404F6A0D84B65, 0x900E7A5542B364B4), new UInt128(0xB00404EA01ABAE95, 0x49998C6B02B364B4) }; + yield return new object[] { new UInt128(0x3006000000000000, 0x07737E4214E44C17), new UInt128(0x30060001431E0FAE, 0x74E5960CB4E44C17) }; + yield return new object[] { new UInt128(0xB02C00013631CF73, 0x2562CDE5A526311A), new UInt128(0xB02C00013631CF73, 0x2562CDE3511A4D1A) }; + yield return new object[] { new UInt128(0x3000000000000000, 0x000DC3F18ACD96FF), new UInt128(0x300004EE2D6D415B, 0x85BAB3728ACD96FF) }; + yield return new object[] { new UInt128(0xB05A000000000000, 0x000000000000002F), new UInt128(0xB040000000000000, 0x0001AB76670B5FFF) }; + yield return new object[] { new UInt128(0x56600000000000DA, 0xB478C74AE7160E75), new UInt128(0x5648C6E937EC9E0B, 0x7316FDA996505000) }; + yield return new object[] { new UInt128(0x30CA000000000000, 0x027CB1C80FA6BE2D), new UInt128(0x30AA585BEE5EDC33, 0xD9EF6DB4E2ED0000) }; + yield return new object[] { new UInt128(0x30120000000002CF, 0x9740029D9E1DA392), new UInt128(0x30120000000017FC, 0x9A07E3E8949DA392) }; + yield return new object[] { new UInt128(0x30111C665687A600, 0x659DFC592140D366), new UInt128(0x30111C66568879C2, 0x816CC946C240D366) }; + yield return new object[] { new UInt128(0x3048000000000000, 0x00000000001799B2), new UInt128(0x3040000000000000, 0x0000000399E3B921) }; + yield return new object[] { new UInt128(0x9028000000000026, 0x8DD5910A10EB5AFD), new UInt128(0x2FFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xAFEA00008584F9E7, 0xA777461B117E33B1), new UInt128(0x2FFDED09BEAD87BD, 0xFA17619EAFF9BE0F) }; + yield return new object[] { new UInt128(0xB03A000000000000, 0x00033EF697D6BABE), new UInt128(0xB03A000000000000, 0x00033EF697D6B6D6) }; + yield return new object[] { new UInt128(0x3032000000000000, 0x000049D9BB8E8CD9), new UInt128(0x3032000000000000, 0x000049D9BC272359) }; + } + + [Theory] + [MemberData(nameof(op_Increment_TestData))] + public static void op_Increment(UInt128 value, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(value); + result++; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Decrement_TestData() + { + yield return new object[] { new UInt128(0xFC00000000000000, 0x0000000000000000), new UInt128(0xFC00000000000000, 0x0000000000000000) }; // NaN + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000000), new UInt128(0x7800000000000000, 0x0000000000000000) }; // +Inf + yield return new object[] { new UInt128(0xF800000000000000, 0x0000000000000000), new UInt128(0xF800000000000000, 0x0000000000000000) }; // -Inf + yield return new object[] { new UInt128(0x7800000000000000, 0x0000000000000002), new UInt128(0x7800000000000000, 0x0000000000000000) }; // non-canonical +Inf (canonicalizes) + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000001) }; // +0 -> -1 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000001) }; // -0 -> -1 + yield return new object[] { new UInt128(0x304A000000000000, 0x0000000000000000), new UInt128(0xB040000000000000, 0x0000000000000001) }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { new UInt128(0x3036000000000000, 0x0000000000000000), new UInt128(0xB036000000000000, 0x00000000000186A0) }; // 0e-5 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000001), new UInt128(0x3040000000000000, 0x0000000000000000) }; // 1 -> 0 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000001), new UInt128(0xB040000000000000, 0x0000000000000002) }; // -1 -> -2 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000002), new UInt128(0x3040000000000000, 0x0000000000000001) }; // 2 + yield return new object[] { new UInt128(0xB040000000000000, 0x0000000000000002), new UInt128(0xB040000000000000, 0x0000000000000003) }; // -2 + yield return new object[] { new UInt128(0x3040000000000000, 0x000000000000000A), new UInt128(0x3040000000000000, 0x0000000000000009) }; // 10 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000005), new UInt128(0xB03E000000000000, 0x0000000000000005) }; // 0.5 -> -0.5 + yield return new object[] { new UInt128(0xB03E000000000000, 0x0000000000000005), new UInt128(0xB03E000000000000, 0x000000000000000F) }; // -0.5 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000019), new UInt128(0x303E000000000000, 0x000000000000000F) }; // 2.5 + yield return new object[] { new UInt128(0x303E000000000000, 0x0000000000000001), new UInt128(0xB03E000000000000, 0x0000000000000009) }; // 0.1 -> -0.9 + yield return new object[] { new UInt128(0x303C000000000000, 0x0000000000000177), new UInt128(0x303C000000000000, 0x0000000000000113) }; // 3.75 + yield return new object[] { new UInt128(0xB03C000000000000, 0x0000000000000019), new UInt128(0xB03C000000000000, 0x000000000000007D) }; // -0.25 + yield return new object[] { new UInt128(0x3040000000000000, 0x0000000000000009), new UInt128(0x3040000000000000, 0x0000000000000008) }; // 9 -> 8 + yield return new object[] { new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x3041ED09BEAD87C0, 0x378D8E63FFFFFFFE) }; // all-nines (decrement in last place) + yield return new object[] { new UInt128(0x3040314DC6448D93, 0x38C15B0A00000000), new UInt128(0x3040314DC6448D93, 0x38C15B09FFFFFFFF) }; // 10^(P-1) + yield return new object[] { new UInt128(0x3040314DC6448D93, 0x38C15B09FFFFFFFF), new UInt128(0x3040314DC6448D93, 0x38C15B09FFFFFFFE) }; // (P-1)-nines + yield return new object[] { new UInt128(0x3088000000000000, 0x0000000000000001), new UInt128(0x3046314DC6448D93, 0x38C15B0A00000000) }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0x5FFFBBBBF868FA2C, 0xFECC335A00000000) }; // near-max (1 negligible) + yield return new object[] { new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000), new UInt128(0xDFFFBBBBF868FA2C, 0xFECC335A00000000) }; // near -max (1 negligible) + yield return new object[] { new UInt128(0x0040000000000000, 0x0000000000000001), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; // tiny positive subnormal (-- ~ -1) + yield return new object[] { new UInt128(0x8040000000000000, 0x0000000000000001), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; // tiny negative subnormal (-- ~ -1) + yield return new object[] { new UInt128(0x3036000000000000, 0x0000000000000001), new UInt128(0xB036000000000000, 0x000000000001869F) }; // 1e-5 + yield return new object[] { new UInt128(0x2FFE41BD085B676E, 0xF657240D55555555), new UInt128(0x2FFE106F4216D9DB, 0xBD95C90355555555) }; // 1.333... full precision + yield return new object[] { new UInt128(0x2FFFED09BEAD87C0, 0x378D8E63FFFFFFFF), new UInt128(0x2FFFBBBBF868FA2C, 0xFECC3359FFFFFFFF) }; // 9.999... full precision + yield return new object[] { new UInt128(0xB0000000000000B1, 0x4C1E4DF5ED261C68), new UInt128(0xB00004EE2D6D420C, 0xD1CB3D76ED261C68) }; + yield return new object[] { new UInt128(0xB02C000000008117, 0x952633E9760D3996), new UInt128(0xB02C000000008117, 0x952633EBCA191D96) }; + yield return new object[] { new UInt128(0xAFF80000000000BC, 0x57570583228BEDE5), new UInt128(0xAFFE314DC6448D93, 0x68F87B8E7BAEBB60) }; + yield return new object[] { new UInt128(0x27C800000029E4B4, 0x709EF360CF7CC897), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0x3062000000000000, 0x0000000000000006), new UInt128(0x3040000000000000, 0x0853A0D2313BFFFF) }; + yield return new object[] { new UInt128(0xB02C000000000000, 0x0000467047DBE843), new UInt128(0xB02C000000000000, 0x000046729BE7CC43) }; + yield return new object[] { new UInt128(0x301204EA7A9BDA00, 0xB6F5E4F86A521036), new UInt128(0x301204EA7A9BC4D3, 0xB42E03AD73D21036) }; + yield return new object[] { new UInt128(0x30020000000085EF, 0xF5A15252168838A9), new UInt128(0xB002007E37BD9A32, 0xCAEFF8D46977C757) }; + yield return new object[] { new UInt128(0x3018000000000000, 0x5DF1A27C94EB2D4F), new UInt128(0xB018000000000005, 0x0DD5BBB0CE24D2B1) }; + yield return new object[] { new UInt128(0x304C000000000000, 0x0000000000000009), new UInt128(0x3040000000000000, 0x000000000089543F) }; + yield return new object[] { new UInt128(0xB0180000003AF28B, 0xE740E213A5668D80), new UInt128(0xB0180000003AF291, 0x5308404108768D80) }; + yield return new object[] { new UInt128(0xB01E000000000000, 0x0000000616E4354B), new UInt128(0xB01E000000000000, 0x0163457E746E354B) }; + yield return new object[] { new UInt128(0x3024000000000000, 0x0000000000000005), new UInt128(0xB024000000000000, 0x00005AF3107A3FFB) }; + yield return new object[] { new UInt128(0xCA2C00012586FEF1, 0xA857CDDF6237FE18), new UInt128(0xCA23BFE31CC37D7E, 0xFA82FB3EB0175F00) }; + yield return new object[] { new UInt128(0xB014000000000000, 0x00000000293EF566), new UInt128(0xB01400000000021E, 0x19E0C9BADB7EF566) }; + yield return new object[] { new UInt128(0x81D4000000000000, 0x5D3FA1D19308E202), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB038000000000000, 0x0000000004FDAAAC), new UInt128(0xB038000000000000, 0x0000000004FDD1BC) }; + yield return new object[] { new UInt128(0xB6D2000000000000, 0x000000000000001D), new UInt128(0xB6928EFB2560675E, 0x2497219D00000000) }; + yield return new object[] { new UInt128(0x3026000101B0C4EA, 0xAB917F63F245E3FF), new UInt128(0x3026000101B0C4EA, 0xAB91764BA3D343FF) }; + yield return new object[] { new UInt128(0xB0140000BC4F01DB, 0x04D54985FC8DE0DB), new UInt128(0xB0140000BC4F03F9, 0x1EB61340AECDE0DB) }; + yield return new object[] { new UInt128(0x3008000000000000, 0x000007AFAEE4D58A), new UInt128(0xB0080000204FCE5E, 0x3E24FAB1611B2A76) }; + yield return new object[] { new UInt128(0x84840000C3B35E24, 0xD944C9B454C06345), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xD9EE000000000000, 0x000000000000005A), new UInt128(0xD9AFBBBBF868FA2C, 0xFECC335A00000000) }; + yield return new object[] { new UInt128(0x2FF6000000000007, 0x1BF19A20781F7D89), new UInt128(0xAFFDED09BEAD87C0, 0x35BBA68E76A9F3D8) }; + yield return new object[] { new UInt128(0x301C0000086A265B, 0x3BC9B1C066BAF9CB), new UInt128(0x301C0000086A265B, 0x2DE8FB0CBF56F9CB) }; + yield return new object[] { new UInt128(0x3030000000000000, 0x000000002A8CA672), new UInt128(0x3030000000000000, 0x000000002496C572) }; + yield return new object[] { new UInt128(0x2FF229E50E8F6A9F, 0xAE76568536C55686), new UInt128(0xAFFDED09A338C1CA, 0xEBDC1499AE8EE461) }; + yield return new object[] { new UInt128(0x4BF6000000000000, 0x0000000000015DFE), new UInt128(0x4BBDB9C093345A45, 0x21A2D72AC0000000) }; + yield return new object[] { new UInt128(0x3010000001BB3680, 0x155F5B37B10809BD), new UInt128(0x3010000001BA62BD, 0xF9908E4A100809BD) }; + yield return new object[] { new UInt128(0xAFF8000000000056, 0xAD502213EE481751), new UInt128(0xAFFE314DC6448D93, 0x4EF1D1DB6D8CDF41) }; + yield return new object[] { new UInt128(0x2FF20060F3D32F96, 0xC08CDAE35E8F6169), new UInt128(0xAFFDED09BE6DFDD4, 0x933EBF6D840BE1D0) }; + yield return new object[] { new UInt128(0x301C000000000000, 0x0A29F51D6081C988), new UInt128(0xB01C000000000000, 0x03B6C19646E23678) }; + yield return new object[] { new UInt128(0xAFFA000000046488, 0x16C345A9670E0BDD), new UInt128(0xAFFE314DC64498D2, 0x05C86DA7DAA16BA3) }; + yield return new object[] { new UInt128(0x2CFA000000000000, 0x00000000003305E2), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0x9646000000F92EA4, 0x0C7335403F583DB1), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB02C005606ABA329, 0xA60456ECE8C0B9E7), new UInt128(0xB02C005606ABA329, 0xA60456EF3CCC9DE7) }; + yield return new object[] { new UInt128(0xB05E000000000000, 0x0000000000000014), new UInt128(0xB040000000000000, 0x00470DE4DF820001) }; + yield return new object[] { new UInt128(0x300A000000000000, 0x004433E77852218C), new UInt128(0xB00A0000033B2E3C, 0x9F8C4C556FADDE74) }; + yield return new object[] { new UInt128(0xB01A000000000000, 0x0000000000000003), new UInt128(0xB01A000000000000, 0x8AC7230489E80003) }; + yield return new object[] { new UInt128(0x303A000000000000, 0x0132602F948B8E0D), new UInt128(0x303A000000000000, 0x0132602F948B8A25) }; + yield return new object[] { new UInt128(0xB00000000016390D, 0xBD0D18E1CAF1E112), new UInt128(0xB00004EE2D837A69, 0x42BA0862CAF1E112) }; + yield return new object[] { new UInt128(0x301A000000000000, 0x00000793A637ABF6), new UInt128(0xB01A000000000000, 0x8AC71B70E3B0540A) }; + yield return new object[] { new UInt128(0x8EA6000000000000, 0x0000000000000037), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xB016000000000175, 0x8A684F948BA6A804), new UInt128(0xB0160000000001AB, 0xC031FD5A6A46A804) }; + yield return new object[] { new UInt128(0xB0260000C03688D2, 0x7AC2450F1321F8C8), new UInt128(0xB0260000C03688D2, 0x7AC24E27619498C8) }; + yield return new object[] { new UInt128(0x5402000000000000, 0x0000000CF771D12A), new UInt128(0x53D51293F938DCC4, 0xF7A5780AF1000000) }; + yield return new object[] { new UInt128(0xB00404F6A0D84B65, 0x900E7A5542B364B4), new UInt128(0xB00405034004E835, 0xD683683F82B364B4) }; + yield return new object[] { new UInt128(0x3006000000000000, 0x07737E4214E44C17), new UInt128(0xB0060001431E0FAE, 0x65FE99888B1BB3E9) }; + yield return new object[] { new UInt128(0xB02C00013631CF73, 0x2562CDE5A526311A), new UInt128(0xB02C00013631CF73, 0x2562CDE7F932151A) }; + yield return new object[] { new UInt128(0x3000000000000000, 0x000DC3F18ACD96FF), new UInt128(0xB00004EE2D6D415B, 0x859F2B8F75326901) }; + yield return new object[] { new UInt128(0xB05A000000000000, 0x000000000000002F), new UInt128(0xB040000000000000, 0x0001AB76670B6001) }; + yield return new object[] { new UInt128(0x56600000000000DA, 0xB478C74AE7160E75), new UInt128(0x5648C6E937EC9E0B, 0x7316FDA996505000) }; + yield return new object[] { new UInt128(0x30CA000000000000, 0x027CB1C80FA6BE2D), new UInt128(0x30AA585BEE5EDC33, 0xD9EF6DB4E2ED0000) }; + yield return new object[] { new UInt128(0x30120000000002CF, 0x9740029D9E1DA392), new UInt128(0xB01200000000125D, 0x6B87DEAD58625C6E) }; + yield return new object[] { new UInt128(0x30111C665687A600, 0x659DFC592140D366), new UInt128(0x30111C665686D23E, 0x49CF2F6B8040D366) }; + yield return new object[] { new UInt128(0x3048000000000000, 0x00000000001799B2), new UInt128(0x3040000000000000, 0x0000000399E3B91F) }; + yield return new object[] { new UInt128(0x9028000000000026, 0x8DD5910A10EB5AFD), new UInt128(0xAFFE314DC6448D93, 0x38C15B0A00000000) }; + yield return new object[] { new UInt128(0xAFEA00008584F9E7, 0xA777461B117E33B1), new UInt128(0xAFFE314DC6448D93, 0x7219F91DBB33D365) }; + yield return new object[] { new UInt128(0xB03A000000000000, 0x00033EF697D6BABE), new UInt128(0xB03A000000000000, 0x00033EF697D6BEA6) }; + yield return new object[] { new UInt128(0x3032000000000000, 0x000049D9BB8E8CD9), new UInt128(0x3032000000000000, 0x000049D9BAF5F659) }; + } + + [Theory] + [MemberData(nameof(op_Decrement_TestData))] + public static void op_Decrement(UInt128 value, UInt128 expected) + { + Decimal128 result = Unsafe.BitCast(value); + result--; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() { NumberStyles style = NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters; @@ -573,5 +1528,45 @@ public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, Nu Assert.False(Decimal128.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out int bytesConsumed)); Assert.Equal(0, bytesConsumed); } + + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal128Arithmetic), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Arithmetic_IntelReferenceVectors(string operation, UInt128 left, UInt128 right, UInt128 expected) + { + Decimal128 l = Unsafe.BitCast(left); + Decimal128 r = Unsafe.BitCast(right); + + Decimal128 result = operation switch + { + "add" => l + r, + "sub" => l - r, + "mul" => l * r, + "div" => l / r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [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) + { + Decimal128 l = Unsafe.BitCast(left); + Decimal128 r = Unsafe.BitCast(right); + + bool result = operation switch + { + "quiet_equal" => l == r, + "quiet_not_equal" => l != r, + "quiet_less" => l < r, + "quiet_greater" => l > r, + "quiet_less_equal" => l <= r, + "quiet_greater_equal" => l >= r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, result); + } } } 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 2c3f70345ed845..c5cac32caa6af8 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,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; @@ -499,6 +499,965 @@ public static IEnumerable NaN_NonCanonicalEncodings_TestData() yield return new object[] { Decimal32.NaN, canonical | 0x03FF_FFFFU }; } + public static IEnumerable Zero_NonCanonicalEncodings_TestData() + { + // A finite encoding whose significand exceeds MaxSignificand (9_999_999) is non-canonical and represents zero. + const uint PositiveZero = 0x3280_0000; + const uint NegativeZero = 0xB280_0000; + + yield return new object[] { PositiveZero, 0x6CBF_FFFFU }; // significand 0x9F_FFFF + yield return new object[] { PositiveZero, 0x6CB8_9680U }; // significand 0x98_9680 (== 10_000_000) + yield return new object[] { NegativeZero, 0xECBF_FFFFU }; + yield return new object[] { NegativeZero, 0xECB8_9680U }; + } + + [Theory] + [MemberData(nameof(Zero_NonCanonicalEncodings_TestData))] + public static void Finite_NonCanonicalEncodings_BehaveAsZero(uint canonicalZero, uint encoding) + { + Decimal32 zero = Unsafe.BitCast(canonicalZero); + Decimal32 nc = Unsafe.BitCast(encoding); + Decimal32 one = Unsafe.BitCast(0x3280_0001U); + Decimal32 inf = Decimal32.PositiveInfinity; + + // A non-canonical finite encoding compares, hashes, and equals as zero. + Assert.Equal(0, zero.CompareTo(nc)); + Assert.True(zero == nc); + Assert.Equal(zero.GetHashCode(), nc.GetHashCode()); + + // Arithmetic treats the non-canonical operand identically to canonical zero. + Assert.Equal(Bits(zero + one), Bits(nc + one)); + Assert.Equal(Bits(zero - one), Bits(nc - one)); + Assert.Equal(Bits(one + zero), Bits(one + nc)); + Assert.Equal(Bits(zero * one), Bits(nc * one)); + Assert.Equal(Bits(one * zero), Bits(one * nc)); + Assert.Equal(Bits(zero / one), Bits(nc / one)); + + // Division-by-zero and invalid operations must trigger for non-canonical zero. + Assert.Equal(Bits(one / zero), Bits(one / nc)); // finite / 0 -> Infinity + Assert.Equal(Bits(zero / zero), Bits(nc / nc)); // 0 / 0 -> NaN + Assert.Equal(Bits(inf * zero), Bits(inf * nc)); // Infinity * 0 -> NaN + + static uint Bits(Decimal32 value) => Unsafe.BitCast(value); + } + + public static IEnumerable UnaryNegation_TestData() + { + yield return new object[] { 0x3280_0000U, 0xB280_0000U }; // +0 -> -0 + yield return new object[] { 0xB280_0000U, 0x3280_0000U }; // -0 -> +0 + yield return new object[] { 0x7800_0000U, 0xF800_0000U }; // +Infinity -> -Infinity + yield return new object[] { 0xF800_0000U, 0x7800_0000U }; // -Infinity -> +Infinity + yield return new object[] { 0xFC00_0000U, 0x7C00_0000U }; // NaN -> sign-flipped NaN + yield return new object[] { 0x7C00_0000U, 0xFC00_0000U }; + } + + [Theory] + [MemberData(nameof(UnaryNegation_TestData))] + public static void op_UnaryNegation(uint value, uint expected) + { + Decimal32 result = -Unsafe.BitCast(value); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Theory] + [InlineData("123")] + [InlineData("-123")] + [InlineData("4567.891")] + [InlineData("-4567.891")] + [InlineData("9.999999e96")] + [InlineData("1e-101")] + public static void op_UnaryNegation_FiniteRoundTrips(string value) + { + Decimal32 d = Decimal32.Parse(value, CultureInfo.InvariantCulture); + Decimal32 negated = -d; + + uint dBits = Unsafe.BitCast(d); + uint negatedBits = Unsafe.BitCast(negated); + + Assert.Equal(dBits ^ 0x8000_0000U, negatedBits); // only the sign bit differs + Assert.Equal(dBits, Unsafe.BitCast(-negated)); // double negation is identity + } + + [Theory] + [InlineData(0x3280_0000U)] // +0 + [InlineData(0xB280_0000U)] // -0 + [InlineData(0x7800_0000U)] // +Infinity + [InlineData(0xF800_0000U)] // -Infinity + [InlineData(0xFC00_0000U)] // NaN + [InlineData(0x00F4_9F87U)] // +9.999999e-90 (arbitrary finite) + public static void op_UnaryPlus(uint value) + { + Decimal32 d = Unsafe.BitCast(value); + Assert.Equal(value, Unsafe.BitCast(+d)); + } + public static IEnumerable op_Addition_TestData() + { + yield return new object[] { 0xFC000000U, 0x32800001U, 0xFC000000U }; // NaN + 1 -> NaN + yield return new object[] { 0x32800001U, 0xFC000000U, 0xFC000000U }; // 1 + NaN -> NaN + yield return new object[] { 0x78000000U, 0x32800001U, 0x78000000U }; // +Inf + 1 -> +Inf + yield return new object[] { 0x32800001U, 0x78000000U, 0x78000000U }; // 1 + +Inf -> +Inf + yield return new object[] { 0x78000000U, 0xF8000000U, 0x7C000000U }; // +Inf + -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000U, 0x78000000U, 0x7C000000U }; // -Inf + +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000U, 0x78000000U, 0x78000000U }; // +Inf + +Inf -> +Inf + yield return new object[] { 0xF8000000U, 0xF8000000U, 0xF8000000U }; // -Inf + -Inf -> -Inf + yield return new object[] { 0x32800000U, 0x32800000U, 0x32800000U }; // +0 + +0 -> +0 + yield return new object[] { 0xB2800000U, 0xB2800000U, 0xB2800000U }; // -0 + -0 -> -0 + yield return new object[] { 0x32800000U, 0xB2800000U, 0x32800000U }; // +0 + -0 -> +0 (round-half-even) + yield return new object[] { 0xB2800000U, 0x32800000U, 0x32800000U }; // -0 + +0 -> +0 + yield return new object[] { 0x32800001U, 0x32800000U, 0x32800001U }; // 1 + 0 -> 1 + yield return new object[] { 0x32800000U, 0x32800001U, 0x32800001U }; // 0 + 1 -> 1 + yield return new object[] { 0x78000002U, 0x32800001U, 0x78000000U }; // non-canonical +Inf + 1 -> canonical +Inf + yield return new object[] { 0x32800001U, 0xF8000005U, 0xF8000000U }; // 1 + non-canonical -Inf -> canonical -Inf + yield return new object[] { 0x7800000FU, 0xF8000003U, 0x7C000000U }; // non-canonical +Inf + non-canonical -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x32800001U, 0x32800002U, 0x32800003U }; // 1 + 2 -> 3 + yield return new object[] { 0x3200000FU, 0x32000019U, 0x32000028U }; // 1.5 + 2.5 -> 4.0 + yield return new object[] { 0x32000001U, 0x32000002U, 0x32000003U }; // 0.1 + 0.2 -> 0.3 + yield return new object[] { 0x32800001U, 0xB2800001U, 0x32800000U }; // 1 + -1 -> +0 + yield return new object[] { 0xB2800001U, 0x32800001U, 0x32800000U }; // -1 + 1 -> +0 + yield return new object[] { 0x6CB8967FU, 0x32800001U, 0x330F4240U }; // all-nines + 1 (carry/overflow to next magnitude) + yield return new object[] { 0x6CB8967FU, 0x6CB8967FU, 0x331E8480U }; // big + big (round) + yield return new object[] { 0x32800001U, 0x2F000001U, 0x2F8F4240U }; // 1 + 1e-P (alignment beyond precision) + yield return new object[] { 0x32800001U, 0x2C800001U, 0x2F8F4240U }; // 1 + tiny (sticky rounding) + yield return new object[] { 0x5F8F4240U, 0x5F8F4240U, 0x5F9E8480U }; // max-ish + max-ish (overflow to Inf) + yield return new object[] { 0x2F92D687U, 0x2FF4CBB1U, 0x6BE7A238U }; // cohort / preferred exponent + yield return new object[] { 0x32800064U, 0x31000001U, 0x310186A1U }; // 100 + 0.001 (exponent spread) + yield return new object[] { 0x6CB8967FU, 0xECB8967EU, 0x32800001U }; // cancellation leaving small + yield return new object[] { 0x2F2C8C35U, 0x2A00002DU, 0x2F2C8C35U }; + yield return new object[] { 0xB8002104U, 0xB98043D6U, 0xB89A82E5U }; + yield return new object[] { 0xA8800010U, 0x34802213U, 0x6CC51A38U }; + yield return new object[] { 0x318496C5U, 0xA71C20DCU, 0x312DE3B2U }; + yield return new object[] { 0x2E830F1EU, 0xA9000005U, 0x2E1E972CU }; + yield return new object[] { 0xAD00006CU, 0xB7011BA9U, 0xB66ECE04U }; + yield return new object[] { 0xAC000017U, 0x380B7DD1U, 0x37F2EA2AU }; + yield return new object[] { 0xBC800009U, 0xA600170BU, 0xEE695440U }; + yield return new object[] { 0x1C8C3773U, 0x4C800FA6U, 0x4B3D2070U }; + yield return new object[] { 0xB2000007U, 0xEAD57BF3U, 0xAF6ACFC0U }; + yield return new object[] { 0xBA000010U, 0x2F0ED152U, 0xB7986A00U }; + yield return new object[] { 0x6A41D272U, 0xA8800006U, 0x6A41D271U }; + yield return new object[] { 0xAC0000EBU, 0x28800027U, 0xAA23DBB0U }; + yield return new object[] { 0xAB8B81BBU, 0xB0000044U, 0xADE7C2CBU }; + yield return new object[] { 0x390001B3U, 0xB885B1AEU, 0xB885A0B0U }; + yield return new object[] { 0x3B800006U, 0x27007EF8U, 0x38DB8D80U }; + yield return new object[] { 0x27034AB4U, 0x3280024CU, 0x30D9B8C0U }; + yield return new object[] { 0x3B800094U, 0x3685C05CU, 0x39969540U }; + yield return new object[] { 0xC0001DD4U, 0x330000DDU, 0xBEF48420U }; + yield return new object[] { 0x378000FAU, 0x9A02AE5DU, 0x35A625A0U }; + yield return new object[] { 0x278014E4U, 0x3806111AU, 0x37BCAB04U }; + yield return new object[] { 0xB20ACE79U, 0x3204CA4CU, 0xB206042DU }; + yield return new object[] { 0xB98025B8U, 0xB2000002U, 0xEE1356C0U }; + yield return new object[] { 0xB780002BU, 0x3B80007DU, 0x399312D0U }; + yield return new object[] { 0xDA81A30AU, 0x38002124U, 0xDA105E64U }; + yield return new object[] { 0xB5800001U, 0x36000006U, 0x3580003BU }; + yield return new object[] { 0xB8800004U, 0x35297F01U, 0xB5B8E2B3U }; + yield return new object[] { 0xAF00252CU, 0xBF000006U, 0xBC5B8D80U }; + yield return new object[] { 0x92EA6215U, 0x38800002U, 0x359E8480U }; + yield return new object[] { 0xAE800008U, 0xAE00009FU, 0xAE0000EFU }; + yield return new object[] { 0x2A0025A7U, 0x35001CE0U, 0x33F0CB00U }; + yield return new object[] { 0x45800056U, 0x32000030U, 0x70C339C0U }; + yield return new object[] { 0x360DAA53U, 0xB20D6A8EU, 0x6D68A73EU }; + yield return new object[] { 0xAF800202U, 0xC000857DU, 0xBF3424D4U }; + yield return new object[] { 0xE1000004U, 0x2D00003DU, 0x2ADD1420U }; + yield return new object[] { 0xBF805674U, 0x6E377AE8U, 0xBEA1C550U }; + yield return new object[] { 0x2E0087F8U, 0x2F0E50D7U, 0x6BAF35FFU }; + yield return new object[] { 0x32800061U, 0x4D80016BU, 0x4BB763B0U }; + yield return new object[] { 0xB1FD9564U, 0x33800008U, 0xB1FC5CE4U }; + yield return new object[] { 0xA9000C89U, 0xC0000CD9U, 0xBEB22FA8U }; + yield return new object[] { 0x37001CE3U, 0x58095C61U, 0x57DD9BCAU }; + yield return new object[] { 0x2E8018F2U, 0xB98002B6U, 0xB7E9E560U }; + yield return new object[] { 0x400B2B74U, 0xAE0003ACU, 0x3FEFB288U }; + yield return new object[] { 0x2B000676U, 0xD1000009U, 0xF3895440U }; + yield return new object[] { 0xDD8E1313U, 0x33325AAEU, 0xF74CBEBEU }; + yield return new object[] { 0x3500008BU, 0x38800030U, 0x36493E01U }; + yield return new object[] { 0x338DB8E1U, 0xB9000023U, 0xB6B567DFU }; + yield return new object[] { 0xB700030FU, 0x2A807402U, 0xB57779F0U }; + yield return new object[] { 0xA68542F3U, 0x35000005U, 0x324C4B40U }; + yield return new object[] { 0x261B9CA7U, 0xB7000005U, 0xB44C4B40U }; + yield return new object[] { 0x33000007U, 0xB180033BU, 0x3180181DU }; + yield return new object[] { 0x0D80010EU, 0xBC000016U, 0xB9A191C0U }; + yield return new object[] { 0x310000DCU, 0xAC0583C6U, 0x2F2191C0U }; + yield return new object[] { 0x297CCD10U, 0x2A000006U, 0x297CCF68U }; + yield return new object[] { 0x15000001U, 0xA7010BBCU, 0xA6689570U }; + yield return new object[] { 0xAB8002D0U, 0xAA84EC44U, 0xAA860584U }; + yield return new object[] { 0xB4000180U, 0x38002093U, 0x36FF3E38U }; + yield return new object[] { 0x2A015FAAU, 0x3A000006U, 0x375B8D80U }; + yield return new object[] { 0x388002C5U, 0x2A04F49DU, 0x36EC2F50U }; + yield return new object[] { 0x35003DFEU, 0x35BB999DU, 0x35BB9FD0U }; + } + + [Theory] + [MemberData(nameof(op_Addition_TestData))] + public static void op_Addition(uint left, uint right, uint expected) + { + Decimal32 result = Unsafe.BitCast(left) + Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Subtraction_TestData() + { + yield return new object[] { 0xFC000000U, 0x32800001U, 0xFC000000U }; // NaN - 1 -> NaN + yield return new object[] { 0x32800001U, 0xFC000000U, 0xFC000000U }; // 1 - NaN -> NaN + yield return new object[] { 0x78000000U, 0x32800001U, 0x78000000U }; // +Inf - 1 -> +Inf + yield return new object[] { 0x32800001U, 0x78000000U, 0xF8000000U }; // 1 - +Inf -> -Inf + yield return new object[] { 0x78000000U, 0xF8000000U, 0x78000000U }; // +Inf - -Inf -> +Inf + yield return new object[] { 0xF8000000U, 0x78000000U, 0xF8000000U }; // -Inf - +Inf -> -Inf + yield return new object[] { 0x78000000U, 0x78000000U, 0x7C000000U }; // +Inf - +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000U, 0xF8000000U, 0x7C000000U }; // -Inf - -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x32800000U, 0x32800000U, 0x32800000U }; // +0 - +0 -> +0 + yield return new object[] { 0xB2800000U, 0xB2800000U, 0x32800000U }; // -0 - -0 -> +0 (round-half-even) + yield return new object[] { 0x32800000U, 0xB2800000U, 0x32800000U }; // +0 - -0 -> +0 + yield return new object[] { 0xB2800000U, 0x32800000U, 0xB2800000U }; // -0 - +0 -> -0 + yield return new object[] { 0x32800001U, 0x32800000U, 0x32800001U }; // 1 - 0 -> 1 + yield return new object[] { 0x32800000U, 0x32800001U, 0xB2800001U }; // 0 - 1 -> -1 + yield return new object[] { 0x78000002U, 0x32800001U, 0x78000000U }; // non-canonical +Inf - 1 -> canonical +Inf + yield return new object[] { 0x32800001U, 0xF8000005U, 0x78000000U }; // 1 - non-canonical -Inf -> canonical +Inf + yield return new object[] { 0x7800000FU, 0xF8000003U, 0x78000000U }; // non-canonical +Inf - non-canonical -Inf -> canonical +Inf + yield return new object[] { 0x32800001U, 0x32800002U, 0xB2800001U }; // 1 - 2 -> -1 + yield return new object[] { 0x3200000FU, 0x32000019U, 0xB200000AU }; // 1.5 - 2.5 -> -1.0 + yield return new object[] { 0x32000001U, 0x32000002U, 0xB2000001U }; // 0.1 - 0.2 -> -0.1 + yield return new object[] { 0x32800001U, 0xB2800001U, 0x32800002U }; // 1 - -1 -> 2 + yield return new object[] { 0xB2800001U, 0x32800001U, 0xB2800002U }; // -1 - 1 -> -2 + yield return new object[] { 0x6CB8967FU, 0x32800001U, 0x6CB8967EU }; // all-nines - 1 -> 9_999_998 + yield return new object[] { 0x6CB8967FU, 0x6CB8967FU, 0x32800000U }; // big - big -> +0 + yield return new object[] { 0x32800001U, 0x2F000001U, 0x6BD8967FU }; // 1 - 1e-7 -> 0.9999999 (alignment beyond precision) + yield return new object[] { 0x32800001U, 0x2C800001U, 0x2F8F4240U }; // 1 - 1e-12 -> 1.000000 (sticky rounding) + yield return new object[] { 0x5F8F4240U, 0x5F8F4240U, 0x5F800000U }; // max-ish - max-ish -> +0 (preferred exponent retained) + yield return new object[] { 0x2F92D687U, 0x2FF4CBB1U, 0xAFE1F52AU }; // cohort / preferred exponent + yield return new object[] { 0x32800064U, 0x31000001U, 0x3101869FU }; // 100 - 0.001 -> 99.999 (exponent spread) + yield return new object[] { 0x6CB8967FU, 0xECB8967EU, 0x331E8480U }; // opposite signs subtract as add: 9_999_999 - -9_999_998 -> 2.0e7 (carry/rounding) + yield return new object[] { 0x2F2C8C35U, 0x2A00002DU, 0x2F2C8C35U }; + yield return new object[] { 0xB8002104U, 0xB98043D6U, 0x389A7C4BU }; + yield return new object[] { 0xA8800010U, 0x34802213U, 0xECC51A38U }; + yield return new object[] { 0x318496C5U, 0xA71C20DCU, 0x312DE3B2U }; + yield return new object[] { 0x2E830F1EU, 0xA9000005U, 0x2E1E972CU }; + yield return new object[] { 0xAD00006CU, 0xB7011BA9U, 0x366ECE04U }; + yield return new object[] { 0xAC000017U, 0x380B7DD1U, 0xB7F2EA2AU }; + yield return new object[] { 0xBC800009U, 0xA600170BU, 0xEE695440U }; + yield return new object[] { 0x1C8C3773U, 0x4C800FA6U, 0xCB3D2070U }; + yield return new object[] { 0xB2000007U, 0xEAD57BF3U, 0xAF6ACFC0U }; + yield return new object[] { 0xBA000010U, 0x2F0ED152U, 0xB7986A00U }; + yield return new object[] { 0x6A41D272U, 0xA8800006U, 0x6A41D273U }; + yield return new object[] { 0xAC0000EBU, 0x28800027U, 0xAA23DBB0U }; + yield return new object[] { 0xAB8B81BBU, 0xB0000044U, 0x2DE7C235U }; + yield return new object[] { 0x390001B3U, 0xB885B1AEU, 0x3885C2ACU }; + yield return new object[] { 0x3B800006U, 0x27007EF8U, 0x38DB8D80U }; + yield return new object[] { 0x27034AB4U, 0x3280024CU, 0xB0D9B8C0U }; + yield return new object[] { 0x3B800094U, 0x3685C05CU, 0x39969540U }; + yield return new object[] { 0xC0001DD4U, 0x330000DDU, 0xBEF48420U }; + yield return new object[] { 0x378000FAU, 0x9A02AE5DU, 0x35A625A0U }; + yield return new object[] { 0x278014E4U, 0x3806111AU, 0xB7BCAB04U }; + yield return new object[] { 0xB20ACE79U, 0x3204CA4CU, 0xB20F98C5U }; + yield return new object[] { 0xB98025B8U, 0xB2000002U, 0xEE1356C0U }; + yield return new object[] { 0xB780002BU, 0x3B80007DU, 0xB99312D0U }; + yield return new object[] { 0xDA81A30AU, 0x38002124U, 0xDA105E64U }; + yield return new object[] { 0xB5800001U, 0x36000006U, 0xB580003DU }; + yield return new object[] { 0xB8800004U, 0x35297F01U, 0xB5C12F4DU }; + yield return new object[] { 0xAF00252CU, 0xBF000006U, 0x3C5B8D80U }; + yield return new object[] { 0x92EA6215U, 0x38800002U, 0xB59E8480U }; + yield return new object[] { 0xAE800008U, 0xAE00009FU, 0x2E00004FU }; + yield return new object[] { 0x2A0025A7U, 0x35001CE0U, 0xB3F0CB00U }; + yield return new object[] { 0x45800056U, 0x32000030U, 0x70C339C0U }; + yield return new object[] { 0x360DAA53U, 0xB20D6A8EU, 0x6D68A73EU }; + yield return new object[] { 0xAF800202U, 0xC000857DU, 0x3F3424D4U }; + yield return new object[] { 0xE1000004U, 0x2D00003DU, 0xAADD1420U }; + yield return new object[] { 0xBF805674U, 0x6E377AE8U, 0xBEA1C550U }; + yield return new object[] { 0x2E0087F8U, 0x2F0E50D7U, 0xEBAF1ACDU }; + yield return new object[] { 0x32800061U, 0x4D80016BU, 0xCBB763B0U }; + yield return new object[] { 0xB1FD9564U, 0x33800008U, 0xB1FECDE4U }; + yield return new object[] { 0xA9000C89U, 0xC0000CD9U, 0x3EB22FA8U }; + yield return new object[] { 0x37001CE3U, 0x58095C61U, 0xD7DD9BCAU }; + yield return new object[] { 0x2E8018F2U, 0xB98002B6U, 0x37E9E560U }; + yield return new object[] { 0x400B2B74U, 0xAE0003ACU, 0x3FEFB288U }; + yield return new object[] { 0x2B000676U, 0xD1000009U, 0x73895440U }; + yield return new object[] { 0xDD8E1313U, 0x33325AAEU, 0xF74CBEBEU }; + yield return new object[] { 0x3500008BU, 0x38800030U, 0xB6493DFFU }; + yield return new object[] { 0x338DB8E1U, 0xB9000023U, 0x36B567E1U }; + yield return new object[] { 0xB700030FU, 0x2A807402U, 0xB57779F0U }; + yield return new object[] { 0xA68542F3U, 0x35000005U, 0xB24C4B40U }; + yield return new object[] { 0x261B9CA7U, 0xB7000005U, 0x344C4B40U }; + yield return new object[] { 0x33000007U, 0xB180033BU, 0x31801E93U }; + yield return new object[] { 0x0D80010EU, 0xBC000016U, 0x39A191C0U }; + yield return new object[] { 0x310000DCU, 0xAC0583C6U, 0x2F2191C0U }; + yield return new object[] { 0x297CCD10U, 0x2A000006U, 0x297CCAB8U }; + yield return new object[] { 0x15000001U, 0xA7010BBCU, 0x26689570U }; + yield return new object[] { 0xAB8002D0U, 0xAA84EC44U, 0x2A83D304U }; + yield return new object[] { 0xB4000180U, 0x38002093U, 0xB6FF3E38U }; + yield return new object[] { 0x2A015FAAU, 0x3A000006U, 0xB75B8D80U }; + yield return new object[] { 0x388002C5U, 0x2A04F49DU, 0x36EC2F50U }; + yield return new object[] { 0x35003DFEU, 0x35BB999DU, 0xB5BB936AU }; + } + + [Theory] + [MemberData(nameof(op_Subtraction_TestData))] + public static void op_Subtraction(uint left, uint right, uint expected) + { + Decimal32 result = Unsafe.BitCast(left) - Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Equality_TestData() + { + yield return new object[] { 0xFC000000U, 0xFC000000U, false }; // NaN == NaN -> false + yield return new object[] { 0xFC000000U, 0x32800001U, false }; // NaN == 1 -> false + yield return new object[] { 0x32800001U, 0xFC000000U, false }; // 1 == NaN -> false + yield return new object[] { 0xFC000000U, 0x78000000U, false }; // NaN == +Inf -> false + yield return new object[] { 0xFE000000U, 0xFE000000U, false }; // sNaN == sNaN -> false + yield return new object[] { 0xFE000000U, 0x32800001U, false }; // sNaN == 1 -> false + yield return new object[] { 0x78000000U, 0x78000000U, true }; // +Inf == +Inf -> true + yield return new object[] { 0xF8000000U, 0xF8000000U, true }; // -Inf == -Inf -> true + yield return new object[] { 0x78000000U, 0xF8000000U, false }; // +Inf == -Inf -> false + yield return new object[] { 0x78000000U, 0x32800001U, false }; // +Inf == 1 -> false + yield return new object[] { 0x78000002U, 0x78000000U, true }; // non-canonical +Inf == +Inf -> true + yield return new object[] { 0xF8000005U, 0xF8000000U, true }; // non-canonical -Inf == -Inf -> true + yield return new object[] { 0x32800000U, 0xB2800000U, true }; // +0 == -0 -> true + yield return new object[] { 0xB2800000U, 0x32800000U, true }; // -0 == +0 -> true + yield return new object[] { 0x32800000U, 0x32800000U, true }; // +0 == +0 -> true + yield return new object[] { 0x32800001U, 0x32800001U, true }; // 1 == 1 -> true + yield return new object[] { 0x32800001U, 0xB2800001U, false }; // 1 == -1 -> false + yield return new object[] { 0xB2800001U, 0x32800001U, false }; // -1 == 1 -> false + yield return new object[] { 0x32800001U, 0x3200000AU, true }; // 1 == 1.0 -> true (cohort) + yield return new object[] { 0x3200000AU, 0x32800001U, true }; // 1.0 == 1 -> true (cohort) + yield return new object[] { 0x31800064U, 0x3200000AU, true }; // 1.00 == 1.0 -> true (cohort) + yield return new object[] { 0x32800064U, 0x33800001U, true }; // 100 == 1e2 -> true (cohort) + yield return new object[] { 0x3280000AU, 0x32800001U, false }; // 10 == 1 -> false + yield return new object[] { 0x5F8F4240U, 0x5F8F4240U, true }; // large == large -> true + yield return new object[] { 0x6CB8967FU, 0x6CB8967FU, true }; // all-nines == all-nines -> true + yield return new object[] { 0x6CB8967FU, 0x6CB8967EU, false }; // all-nines == near -> false + yield return new object[] { 0x32000001U, 0x32000001U, true }; // 0.1 == 0.1 -> true + yield return new object[] { 0xB2000001U, 0x32000001U, false }; // -0.1 == 0.1 -> false + yield return new object[] { 0x35000000U, 0x32800000U, true }; // +0 (exp 5) == +0 -> true (zero cohort) + yield return new object[] { 0x2F2C8C35U, 0xBD800001U, false }; + yield return new object[] { 0x2A80BFA3U, 0x2E80F146U, false }; + yield return new object[] { 0xEDA7AB71U, 0x33800054U, false }; + yield return new object[] { 0xA71C20DCU, 0x2C8C18CBU, false }; + yield return new object[] { 0xB48003B6U, 0xB3817318U, true }; + yield return new object[] { 0xB980002FU, 0x4CC66EF1U, false }; + yield return new object[] { 0x3171BC43U, 0x28803E6EU, false }; + yield return new object[] { 0xB9855F56U, 0xB935B95CU, true }; + yield return new object[] { 0xA80003C8U, 0xA7017A20U, true }; + yield return new object[] { 0x08000005U, 0x070001F4U, true }; + yield return new object[] { 0x3A000116U, 0xBA000010U, false }; + yield return new object[] { 0x2F0ED152U, 0x8E003D25U, false }; + yield return new object[] { 0x2E80ADBAU, 0x29800030U, false }; + yield return new object[] { 0xAD80005DU, 0xAC802454U, true }; + yield return new object[] { 0x33415C3BU, 0xB80006F3U, false }; + yield return new object[] { 0x3B800006U, 0x58802513U, false }; + yield return new object[] { 0x27034AB4U, 0x26A0EB08U, true }; + yield return new object[] { 0x30DA8988U, 0xB187B561U, false }; + yield return new object[] { 0xB308524FU, 0xB2D33716U, true }; + yield return new object[] { 0xB0001165U, 0xAF80ADF2U, true }; + yield return new object[] { 0x168020CDU, 0x158CD014U, true }; + yield return new object[] { 0x278014E4U, 0xAD00004EU, false }; + yield return new object[] { 0xB6F4EDE0U, 0xBB000007U, false }; + yield return new object[] { 0xB2000002U, 0xB10000C8U, true }; + yield return new object[] { 0xA987E16CU, 0xA94ECE38U, true }; + yield return new object[] { 0xA9006A4FU, 0xA8842716U, true }; + yield return new object[] { 0x28000036U, 0x27001518U, true }; + yield return new object[] { 0xAE800293U, 0xAD81016CU, true }; + yield return new object[] { 0x36000006U, 0xCA87FEFCU, false }; + yield return new object[] { 0x33011190U, 0xAC001D41U, false }; + yield return new object[] { 0x5C800033U, 0xAA006FD9U, false }; + yield return new object[] { 0xBA800003U, 0xB980012CU, true }; + yield return new object[] { 0x3A80001BU, 0x39006978U, true }; + yield return new object[] { 0x3409DBACU, 0x33E294B8U, true }; + yield return new object[] { 0x6DD212C6U, 0xB2002198U, false }; + yield return new object[] { 0x4652D371U, 0x2F010631U, false }; + yield return new object[] { 0xB48002F6U, 0xB3812818U, true }; + yield return new object[] { 0xB98003C0U, 0xB80EA600U, true }; + yield return new object[] { 0x49800395U, 0x48816634U, true }; + yield return new object[] { 0x34800368U, 0x2E800341U, false }; + yield return new object[] { 0xB80000CEU, 0xB7005078U, true }; + yield return new object[] { 0x39800003U, 0xB300195BU, false }; + yield return new object[] { 0x33800008U, 0x33000050U, true }; + yield return new object[] { 0x41800251U, 0x4080E7A4U, true }; + yield return new object[] { 0x358001C7U, 0xAF000009U, false }; + yield return new object[] { 0xB2000F58U, 0xB70EF86AU, false }; + yield return new object[] { 0x400B2B74U, 0x3FEFB288U, true }; + yield return new object[] { 0x28B41FEFU, 0xD1000009U, false }; + yield return new object[] { 0xDD8E1313U, 0x30001D6CU, false }; + yield return new object[] { 0xB06FD858U, 0xA888673CU, false }; + yield return new object[] { 0xB80004B6U, 0xB700011AU, false }; + yield return new object[] { 0x2A807402U, 0x3180012EU, false }; + yield return new object[] { 0x180002F8U, 0x17801DB0U, true }; + yield return new object[] { 0x94800019U, 0x938009C4U, true }; + yield return new object[] { 0xB000F1D7U, 0xAF5E77FCU, true }; + yield return new object[] { 0xAA800003U, 0xB7000051U, false }; + yield return new object[] { 0x3180001AU, 0xB60002EEU, false }; + yield return new object[] { 0x07D3490CU, 0x2B80D4D0U, false }; + yield return new object[] { 0xAF000001U, 0xAE000064U, true }; + yield return new object[] { 0xAB8002D0U, 0xB0800377U, false }; + } + + [Theory] + [MemberData(nameof(op_Equality_TestData))] + public static void op_Equality(uint left, uint right, bool expected) + { + Decimal32 l = Unsafe.BitCast(left); + Decimal32 r = Unsafe.BitCast(right); + Assert.Equal(expected, l == r); + Assert.Equal(!expected, l != r); + } + + public static IEnumerable op_Comparison_TestData() + { + yield return new object[] { 0xFC000000U, 0x32800001U, false, false, false, false }; // NaN vs 1 -> unordered + yield return new object[] { 0x32800001U, 0xFC000000U, false, false, false, false }; // 1 vs NaN -> unordered + yield return new object[] { 0xFC000000U, 0xFC000000U, false, false, false, false }; // NaN vs NaN -> unordered + yield return new object[] { 0xFE000000U, 0x32800001U, false, false, false, false }; // sNaN vs 1 -> unordered + yield return new object[] { 0xFC000000U, 0x78000000U, false, false, false, false }; // NaN vs +Inf -> unordered + yield return new object[] { 0x78000000U, 0x32800001U, false, true, false, true }; // +Inf vs 1 + yield return new object[] { 0x32800001U, 0x78000000U, true, false, true, false }; // 1 vs +Inf + yield return new object[] { 0xF8000000U, 0x32800001U, true, false, true, false }; // -Inf vs 1 + yield return new object[] { 0x32800001U, 0xF8000000U, false, true, false, true }; // 1 vs -Inf + yield return new object[] { 0xF8000000U, 0x78000000U, true, false, true, false }; // -Inf vs +Inf + yield return new object[] { 0x78000000U, 0x78000000U, false, false, true, true }; // +Inf vs +Inf (equal) + yield return new object[] { 0xF8000000U, 0xF8000000U, false, false, true, true }; // -Inf vs -Inf (equal) + yield return new object[] { 0x78000002U, 0x78000000U, false, false, true, true }; // non-canonical +Inf vs +Inf (equal) + yield return new object[] { 0x32800000U, 0xB2800000U, false, false, true, true }; // +0 vs -0 (equal) + yield return new object[] { 0xB2800000U, 0x32800000U, false, false, true, true }; // -0 vs +0 (equal) + yield return new object[] { 0x32800000U, 0x32800001U, true, false, true, false }; // 0 vs 1 + yield return new object[] { 0xB2800001U, 0x32800000U, true, false, true, false }; // -1 vs 0 + yield return new object[] { 0x32800001U, 0x32800002U, true, false, true, false }; // 1 vs 2 + yield return new object[] { 0x32800002U, 0x32800001U, false, true, false, true }; // 2 vs 1 + yield return new object[] { 0xB2800001U, 0x32800001U, true, false, true, false }; // -1 vs 1 + yield return new object[] { 0x32800001U, 0xB2800001U, false, true, false, true }; // 1 vs -1 + yield return new object[] { 0xB2800001U, 0xB2800002U, false, true, false, true }; // -1 vs -2 + yield return new object[] { 0x32800001U, 0x3200000AU, false, false, true, true }; // 1 vs 1.0 (cohort equal) + yield return new object[] { 0x3200000AU, 0x32800001U, false, false, true, true }; // 1.0 vs 1 (cohort equal) + yield return new object[] { 0x32800064U, 0x33800001U, false, false, true, true }; // 100 vs 1e2 (cohort equal) + yield return new object[] { 0x3280000AU, 0x32800001U, false, true, false, true }; // 10 vs 1 + yield return new object[] { 0x32000001U, 0x32000002U, true, false, true, false }; // 0.1 vs 0.2 + yield return new object[] { 0x6BF8967FU, 0x6BF8967EU, false, true, false, true }; // close values + yield return new object[] { 0x6CB8967FU, 0x6CB8967EU, false, true, false, true }; // all-nines vs near + yield return new object[] { 0x5F8F4240U, 0x5F8186A0U, false, true, false, true }; // large magnitudes + yield return new object[] { 0xDF8F4240U, 0x5F8F4240U, true, false, true, false }; // -large vs +large + yield return new object[] { 0x2F2C8C35U, 0xBD800001U, false, true, false, true }; + yield return new object[] { 0x2A80BFA3U, 0x2E80F146U, true, false, true, false }; + yield return new object[] { 0xEDA7AB71U, 0x33800054U, true, false, true, false }; + yield return new object[] { 0xA71C20DCU, 0x2C8C18CBU, true, false, true, false }; + yield return new object[] { 0xB48003B6U, 0xB3817318U, false, false, true, true }; + yield return new object[] { 0xB980002FU, 0x4CC66EF1U, true, false, true, false }; + yield return new object[] { 0x3171BC43U, 0x28803E6EU, false, true, false, true }; + yield return new object[] { 0xB9855F56U, 0xB935B95CU, false, false, true, true }; + yield return new object[] { 0xA80003C8U, 0xA7017A20U, false, false, true, true }; + yield return new object[] { 0x08000005U, 0x39801340U, true, false, true, false }; + yield return new object[] { 0xCA000C33U, 0x29000379U, true, false, true, false }; + yield return new object[] { 0x33000001U, 0x31800012U, false, true, false, true }; + yield return new object[] { 0x36000003U, 0x340C7831U, true, false, true, false }; + yield return new object[] { 0xAE808F1FU, 0x3782B3D3U, true, false, true, false }; + yield return new object[] { 0xB885B1AEU, 0xB838F0CCU, false, false, true, true }; + yield return new object[] { 0x2A80014CU, 0x29800003U, false, true, false, true }; + yield return new object[] { 0x3280024CU, 0x3A083505U, true, false, true, false }; + yield return new object[] { 0x3685C05CU, 0x31000002U, false, true, false, true }; + yield return new object[] { 0x300090CAU, 0x6543374AU, false, true, false, true }; + yield return new object[] { 0xB4800019U, 0xAD847994U, true, false, true, false }; + yield return new object[] { 0xAD00004EU, 0x33001329U, true, false, true, false }; + yield return new object[] { 0xB98025B8U, 0xB88EBBE0U, false, false, true, true }; + yield return new object[] { 0xAF8E5E1FU, 0xEBCFAD36U, false, false, true, true }; + yield return new object[] { 0xA987E16CU, 0xA94ECE38U, false, false, true, true }; + yield return new object[] { 0xA9006A4FU, 0x30000002U, true, false, true, false }; + yield return new object[] { 0xB2950EABU, 0x27802665U, true, false, true, false }; + yield return new object[] { 0xBA800028U, 0xAA05B8ADU, true, false, true, false }; + yield return new object[] { 0xB8013769U, 0xB779A504U, false, false, true, true }; + yield return new object[] { 0xB680005AU, 0xB6000384U, false, false, true, true }; + yield return new object[] { 0x2B000471U, 0xAE800008U, false, true, false, true }; + yield return new object[] { 0xAE00009FU, 0xDD9AF653U, false, true, false, true }; + yield return new object[] { 0x3A800006U, 0x3A00003CU, false, false, true, true }; + yield return new object[] { 0xB2002198U, 0xB9000008U, false, true, false, true }; + yield return new object[] { 0xA8000116U, 0x2E80D42FU, true, false, true, false }; + yield return new object[] { 0x8C801CF2U, 0x8B711150U, false, false, true, true }; + yield return new object[] { 0x49800395U, 0x48816634U, false, false, true, true }; + yield return new object[] { 0x34800368U, 0x2E800341U, false, true, false, true }; + yield return new object[] { 0xB80000CEU, 0xB7005078U, false, false, true, true }; + yield return new object[] { 0x39800003U, 0xB300195BU, false, true, false, true }; + yield return new object[] { 0x33800008U, 0xB7000028U, false, true, false, true }; + yield return new object[] { 0xBF033649U, 0x35F38F19U, true, false, true, false }; + yield return new object[] { 0x58095C61U, 0xB0BD637DU, false, true, false, true }; + yield return new object[] { 0x6BB0F7CAU, 0x36013C4DU, true, false, true, false }; + yield return new object[] { 0x530001F8U, 0xD1000009U, false, true, false, true }; + yield return new object[] { 0xDD8E1313U, 0x30001D6CU, true, false, true, false }; + yield return new object[] { 0xB06FD858U, 0xA888673CU, true, false, true, false }; + yield return new object[] { 0xB80004B6U, 0xB700011AU, true, false, true, false }; + yield return new object[] { 0x2A807402U, 0x3180012EU, true, false, true, false }; + yield return new object[] { 0x180002F8U, 0x2E000002U, true, false, true, false }; + yield return new object[] { 0xCC801631U, 0xB180033BU, true, false, true, false }; + yield return new object[] { 0x0D80010EU, 0xBB800002U, false, true, false, true }; + yield return new object[] { 0x310000DCU, 0xB60002EEU, false, true, false, true }; + yield return new object[] { 0x07D3490CU, 0x2B0EF5D7U, true, false, true, false }; + yield return new object[] { 0xAF000001U, 0xAE000064U, false, false, true, true }; + yield return new object[] { 0xAB8002D0U, 0xB0800377U, false, true, false, true }; + yield return new object[] { 0xB6802125U, 0xEDA8258FU, false, true, false, true }; + yield return new object[] { 0xAC000001U, 0xAB0DD4BDU, false, true, false, true }; + yield return new object[] { 0x34011190U, 0x2A00159EU, false, true, false, true }; + yield return new object[] { 0xAD01FF37U, 0x37837CF9U, true, false, true, false }; + yield return new object[] { 0xB58001C6U, 0x2D000A26U, true, false, true, false }; + } + + [Theory] + [MemberData(nameof(op_Comparison_TestData))] + public static void op_Comparison(uint left, uint right, bool lessThan, bool greaterThan, bool lessThanOrEqual, bool greaterThanOrEqual) + { + Decimal32 l = Unsafe.BitCast(left); + Decimal32 r = Unsafe.BitCast(right); + Assert.Equal(lessThan, l < r); + Assert.Equal(greaterThan, l > r); + Assert.Equal(lessThanOrEqual, l <= r); + Assert.Equal(greaterThanOrEqual, l >= r); + } + + public static IEnumerable op_Multiply_TestData() + { + yield return new object[] { 0xFC000000U, 0x32800001U, 0xFC000000U }; // NaN * 1 -> NaN + yield return new object[] { 0x32800001U, 0xFC000000U, 0xFC000000U }; // 1 * NaN -> NaN + yield return new object[] { 0xFC000000U, 0x78000000U, 0xFC000000U }; // NaN * +Inf -> NaN + yield return new object[] { 0x78000000U, 0x32800000U, 0x7C000000U }; // +Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x32800000U, 0x78000000U, 0x7C000000U }; // +0 * +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000U, 0x32800000U, 0x7C000000U }; // -Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000U, 0xB2800000U, 0x7C000000U }; // +Inf * -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000U, 0x32800001U, 0x78000000U }; // +Inf * 1 -> +Inf + yield return new object[] { 0x78000000U, 0xB2800001U, 0xF8000000U }; // +Inf * -1 -> -Inf + yield return new object[] { 0xF8000000U, 0x32800002U, 0xF8000000U }; // -Inf * 2 -> -Inf + yield return new object[] { 0xF8000000U, 0xB2800002U, 0x78000000U }; // -Inf * -2 -> +Inf + yield return new object[] { 0x78000000U, 0x78000000U, 0x78000000U }; // +Inf * +Inf -> +Inf + yield return new object[] { 0x78000000U, 0xF8000000U, 0xF8000000U }; // +Inf * -Inf -> -Inf + yield return new object[] { 0xF8000000U, 0xF8000000U, 0x78000000U }; // -Inf * -Inf -> +Inf + yield return new object[] { 0x78000002U, 0x32800001U, 0x78000000U }; // non-canonical +Inf * 1 -> canonical +Inf + yield return new object[] { 0x32800001U, 0xF8000005U, 0xF8000000U }; // 1 * non-canonical -Inf -> canonical -Inf + yield return new object[] { 0x32800000U, 0x32800000U, 0x32800000U }; // +0 * +0 -> +0 + yield return new object[] { 0xB2800000U, 0xB2800000U, 0x32800000U }; // -0 * -0 -> +0 + yield return new object[] { 0x32800000U, 0xB2800000U, 0xB2800000U }; // +0 * -0 -> -0 + yield return new object[] { 0x32800001U, 0xB2800000U, 0xB2800000U }; // 1 * -0 -> -0 + yield return new object[] { 0xB2800001U, 0x32800000U, 0xB2800000U }; // -1 * +0 -> -0 + yield return new object[] { 0x35000000U, 0x34000000U, 0x36800000U }; // 0e5 * 0e3 -> 0e8 (preferred exp) + yield return new object[] { 0x30000000U, 0x3280000CU, 0x30000000U }; // 0e-5 * 12 -> 0e-5 (preferred exp) + yield return new object[] { 0x32800002U, 0x32800005U, 0x3280000AU }; // 2 * 5 -> 10 (exp 0, trailing zero retained) + yield return new object[] { 0x32800002U, 0x32000005U, 0x3200000AU }; // 2 * 0.5 -> 1.0 + yield return new object[] { 0x3200000FU, 0x32800002U, 0x3200001EU }; // 1.5 * 2 -> 3.0 + yield return new object[] { 0x32000001U, 0x32000001U, 0x31800001U }; // 0.1 * 0.1 -> 0.01 + yield return new object[] { 0x3200000CU, 0x3200000CU, 0x31800090U }; // 1.2 * 1.2 -> 1.44 + yield return new object[] { 0xB2800003U, 0x32800007U, 0xB2800015U }; // -3 * 7 -> -21 + yield return new object[] { 0x3280000CU, 0x3280000CU, 0x32800090U }; // 12 * 12 -> 144 + yield return new object[] { 0x6CB8967FU, 0x6CB8967FU, 0x6D98967EU }; // all-nines squared (round) + yield return new object[] { 0x2F945855U, 0x2F9B2071U, 0x2FA42B41U }; // full-precision * full-precision (round) + yield return new object[] { 0x328F4240U, 0x328F4240U, 0x358F4240U }; // 10^(P-1) * 10^(P-1) (trailing zeros beyond precision) + yield return new object[] { 0x77E95440U, 0x77E95440U, 0x78000000U }; // huge * huge -> +Inf (overflow) + yield return new object[] { 0xF7E95440U, 0x77E95440U, 0xF8000000U }; // -huge * huge -> -Inf (overflow) + yield return new object[] { 0x02800001U, 0x02800001U, 0x00000000U }; // tiny * tiny -> underflow + yield return new object[] { 0x03000001U, 0x30000001U, 0x00800001U }; // small * small (subnormal-ish) + yield return new object[] { 0x6618967FU, 0x6638967FU, 0x6078967EU }; // wide product just below min quantum (normal after single rounding) + yield return new object[] { 0x65B8967FU, 0x65D8967FU, 0x00002710U }; // wide product deep in the subnormal range + yield return new object[] { 0x60B8967FU, 0x60B8967FU, 0x00000000U }; // wide product underflow to zero/epsilon + yield return new object[] { 0x5F800000U, 0x5F800005U, 0x5F800000U }; // zero * finite, preferred exponent far above max quantum (clamped) + yield return new object[] { 0xDF800000U, 0x5F800005U, 0xDF800000U }; // -zero * finite, preferred exponent clamped (sign preserved) + yield return new object[] { 0x33000000U, 0x5F800005U, 0x5F800000U }; // zero * finite, preferred exponent one above max quantum (clamped) + yield return new object[] { 0x32800000U, 0x5F800005U, 0x5F800000U }; // zero * finite, preferred exponent exactly at max quantum (no clamp) + yield return new object[] { 0x2F2C8C35U, 0x2A00002DU, 0x27940BE5U }; + yield return new object[] { 0xB8002104U, 0xB98043D6U, 0x4016657EU }; + yield return new object[] { 0xA8800010U, 0x34802213U, 0xAA822130U }; + yield return new object[] { 0x318496C5U, 0xA71C20DCU, 0xA8D497F0U }; + yield return new object[] { 0x2E830F1EU, 0xA9000005U, 0xA50F4B96U }; + yield return new object[] { 0xAD00006CU, 0xB7011BA9U, 0x31F7AB4CU }; + yield return new object[] { 0xAC000017U, 0x380B7DD1U, 0xB21A6E2EU }; + yield return new object[] { 0xBC800009U, 0xA600170BU, 0x3000CF63U }; + yield return new object[] { 0x1C8C3773U, 0x4C800FA6U, 0x3830F090U }; + yield return new object[] { 0xB2000007U, 0xEAD57BF3U, 0x2B68A390U }; + yield return new object[] { 0xBA000010U, 0x2F0ED152U, 0xB717B550U }; + yield return new object[] { 0x6A41D272U, 0xA8800006U, 0x9FCDE4ABU }; + yield return new object[] { 0xAC0000EBU, 0x28800027U, 0xA20023CDU }; + yield return new object[] { 0xAB8B81BBU, 0xB0000044U, 0x29CE3EF8U }; + yield return new object[] { 0x390001B3U, 0xB885B1AEU, 0xC018C4E8U }; + yield return new object[] { 0x3B800006U, 0x27007EF8U, 0x3002F9D0U }; + yield return new object[] { 0x27034AB4U, 0x3280024CU, 0x28135B18U }; + yield return new object[] { 0x3B800094U, 0x3685C05CU, 0x40551EEBU }; + yield return new object[] { 0xC0001DD4U, 0x330000DDU, 0xC099C004U }; + yield return new object[] { 0x378000FAU, 0x9A02AE5DU, 0x9FC30715U }; + yield return new object[] { 0x278014E4U, 0x3806111AU, 0x2EA071FDU }; + yield return new object[] { 0xB20ACE79U, 0x3204CA4CU, 0xB421ECD8U }; + yield return new object[] { 0xB98025B8U, 0xB2000002U, 0x39004B70U }; + yield return new object[] { 0xB780002BU, 0x3B80007DU, 0xC08014FFU }; + yield return new object[] { 0xDA81A30AU, 0x38002124U, 0xF8000000U }; + yield return new object[] { 0xB5800001U, 0x36000006U, 0xB9000006U }; + yield return new object[] { 0xB8800004U, 0x35297F01U, 0xBB909934U }; + yield return new object[] { 0xAF00252CU, 0xBF000006U, 0x3B80DF08U }; + yield return new object[] { 0x92EA6215U, 0x38800002U, 0x991546D1U }; + yield return new object[] { 0xAE800008U, 0xAE00009FU, 0x2A0004F8U }; + yield return new object[] { 0x2A0025A7U, 0x35001CE0U, 0x2D6CB89DU }; + yield return new object[] { 0x45800056U, 0x32000030U, 0x45001020U }; + yield return new object[] { 0x360DAA53U, 0xB20D6A8EU, 0xB87826E0U }; + yield return new object[] { 0xAF800202U, 0xC000857DU, 0x3D9ACD4CU }; + yield return new object[] { 0xE1000004U, 0x2D00003DU, 0x8007CED9U }; + yield return new object[] { 0xBF805674U, 0x6E377AE8U, 0xC821868CU }; + yield return new object[] { 0x2E0087F8U, 0x2F0E50D7U, 0x2CB1D493U }; + yield return new object[] { 0x32800061U, 0x4D80016BU, 0x4D80898BU }; + yield return new object[] { 0xB1FD9564U, 0x33800008U, 0xB3647783U }; + yield return new object[] { 0xA9000C89U, 0xC0000CD9U, 0x37101AD0U }; + yield return new object[] { 0x37001CE3U, 0x58095C61U, 0x5E453939U }; + yield return new object[] { 0x2E8018F2U, 0xB98002B6U, 0xB5C3A00CU }; + yield return new object[] { 0x400B2B74U, 0xAE0003ACU, 0xBCE8FEDCU }; + yield return new object[] { 0x2B000676U, 0xD1000009U, 0xC9803A26U }; + yield return new object[] { 0xDD8E1313U, 0x33325AAEU, 0xF8000000U }; + yield return new object[] { 0x3500008BU, 0x38800030U, 0x3B001A10U }; + yield return new object[] { 0x338DB8E1U, 0xB9000023U, 0xBAB00714U }; + yield return new object[] { 0xB700030FU, 0x2A807402U, 0xAFA37B69U }; + yield return new object[] { 0xA68542F3U, 0x35000005U, 0xA91A4EBFU }; + yield return new object[] { 0x261B9CA7U, 0xB7000005U, 0xEAAA0F43U }; + yield return new object[] { 0x33000007U, 0xB180033BU, 0xB200169DU }; + yield return new object[] { 0x0D80010EU, 0xBC000016U, 0x97001734U }; + yield return new object[] { 0x310000DCU, 0xAC0583C6U, 0xAB795304U }; + yield return new object[] { 0x297CCD10U, 0x2A000006U, 0x214AE170U }; + yield return new object[] { 0x15000001U, 0xA7010BBCU, 0x89810BBCU }; + yield return new object[] { 0xAB8002D0U, 0xAA84EC44U, 0x24A371EAU }; + yield return new object[] { 0xB4000180U, 0x38002093U, 0xB9B0DC80U }; + yield return new object[] { 0x2A015FAAU, 0x3A000006U, 0x31883DFCU }; + yield return new object[] { 0x388002C5U, 0x2A04F49DU, 0x31232278U }; + yield return new object[] { 0x35003DFEU, 0x35BB999DU, 0x3A5E95D5U }; + } + + [Theory] + [MemberData(nameof(op_Multiply_TestData))] + public static void op_Multiply(uint left, uint right, uint expected) + { + Decimal32 result = Unsafe.BitCast(left) * Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Division_TestData() + { + yield return new object[] { 0xFC000000U, 0x32800001U, 0xFC000000U }; // NaN / 1 -> NaN + yield return new object[] { 0x32800001U, 0xFC000000U, 0xFC000000U }; // 1 / NaN -> NaN + yield return new object[] { 0xFC000000U, 0x78000000U, 0xFC000000U }; // NaN / +Inf -> NaN + yield return new object[] { 0x78000000U, 0x78000000U, 0x7C000000U }; // +Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000U, 0xF8000000U, 0x7C000000U }; // +Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000U, 0x78000000U, 0x7C000000U }; // -Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000U, 0xF8000000U, 0x7C000000U }; // -Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000U, 0x32800001U, 0x78000000U }; // +Inf / 1 -> +Inf + yield return new object[] { 0x78000000U, 0xB2800001U, 0xF8000000U }; // +Inf / -1 -> -Inf + yield return new object[] { 0xF8000000U, 0x32800002U, 0xF8000000U }; // -Inf / 2 -> -Inf + yield return new object[] { 0xF8000000U, 0xB2800002U, 0x78000000U }; // -Inf / -2 -> +Inf + yield return new object[] { 0x78000000U, 0x32800000U, 0x78000000U }; // +Inf / +0 -> +Inf + yield return new object[] { 0xF8000000U, 0x32800000U, 0xF8000000U }; // -Inf / +0 -> -Inf + yield return new object[] { 0x78000000U, 0xB2800000U, 0xF8000000U }; // +Inf / -0 -> -Inf + yield return new object[] { 0x32800001U, 0x78000000U, 0x00000000U }; // 1 / +Inf -> +0 (Etiny) + yield return new object[] { 0xB2800001U, 0x78000000U, 0x80000000U }; // -1 / +Inf -> -0 (Etiny) + yield return new object[] { 0x32800005U, 0xF8000000U, 0x80000000U }; // 5 / -Inf -> -0 (Etiny) + yield return new object[] { 0x37800005U, 0x78000000U, 0x00000000U }; // 5e10 / +Inf -> +0 (Etiny, dividend exp ignored) + yield return new object[] { 0x32800000U, 0x78000000U, 0x00000000U }; // +0 / +Inf -> +0 (Etiny) + yield return new object[] { 0xB2800000U, 0x78000000U, 0x80000000U }; // -0 / +Inf -> -0 (Etiny) + yield return new object[] { 0x32800005U, 0x32800000U, 0x78000000U }; // 5 / +0 -> +Inf + yield return new object[] { 0xB2800005U, 0x32800000U, 0xF8000000U }; // -5 / +0 -> -Inf + yield return new object[] { 0x32800005U, 0xB2800000U, 0xF8000000U }; // 5 / -0 -> -Inf + yield return new object[] { 0xB2800005U, 0xB2800000U, 0x78000000U }; // -5 / -0 -> +Inf + yield return new object[] { 0x32800000U, 0x32800000U, 0x7C000000U }; // +0 / +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xB2800000U, 0xB2800000U, 0x7C000000U }; // -0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x32800000U, 0xB2800000U, 0x7C000000U }; // +0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x32800000U, 0x32800005U, 0x32800000U }; // +0 / 5 -> +0 (ideal exp) + yield return new object[] { 0xB2800000U, 0x32800005U, 0xB2800000U }; // -0 / 5 -> -0 (ideal exp, sign xor) + yield return new object[] { 0x33800000U, 0x32800005U, 0x33800000U }; // 0e2 / 5 -> 0e2 (ideal exp) + yield return new object[] { 0x32800000U, 0x34000005U, 0x31000000U }; // 0 / 5e3 -> 0e-3 (ideal exp) + yield return new object[] { 0x35000000U, 0x30000005U, 0x37800000U }; // 0e5 / 5e-5 -> 0e10 (ideal exp) + yield return new object[] { 0x5F800000U, 0x30000005U, 0x5F800000U }; // zero dividend, ideal exp far above max quantum (clamped) + yield return new object[] { 0x32800001U, 0x32800002U, 0x32000005U }; // 1 / 2 -> 0.5 (exact) + yield return new object[] { 0x32800001U, 0x32800008U, 0x3100007DU }; // 1 / 8 -> 0.125 (exact) + yield return new object[] { 0x32800064U, 0x32800004U, 0x32800019U }; // 100 / 4 -> 25 (exact) + yield return new object[] { 0x3280000AU, 0x32800002U, 0x32800005U }; // 10 / 2 -> 5 (exact) + yield return new object[] { 0x32800006U, 0x32800003U, 0x32800002U }; // 6 / 3 -> 2 (exact) + yield return new object[] { 0x32800005U, 0x32800005U, 0x32800001U }; // 5 / 5 -> 1 (exact) + yield return new object[] { 0x31800064U, 0x32800004U, 0x31800019U }; // 1.00 / 4 -> 0.25 (exact, ideal exp preserved) + yield return new object[] { 0x32800007U, 0x32800002U, 0x32000023U }; // 7 / 2 -> 3.5 (exact) + yield return new object[] { 0xB280000FU, 0x32800004U, 0xB1800177U }; // -15 / 4 -> -3.75 (exact, sign) + yield return new object[] { 0x32800001U, 0x32800003U, 0x2F32DCD5U }; // 1 / 3 -> repeating (round) + yield return new object[] { 0x32800002U, 0x32800003U, 0x2F65B9ABU }; // 2 / 3 -> repeating (round) + yield return new object[] { 0x328F4240U, 0x32800007U, 0x3215CC5BU }; // 1000000 / 7 (round) + yield return new object[] { 0x3280000AU, 0x32800003U, 0x2FB2DCD5U }; // 10 / 3 -> repeating (round) + yield return new object[] { 0x6CB8967FU, 0x32800007U, 0x3295CC5BU }; // all-nines / 7 (round) + yield return new object[] { 0x32800001U, 0x6CB8967FU, 0x2C0F4240U }; // 1 / all-nines (round) + yield return new object[] { 0x32800002U, 0x32800007U, 0x2F2B98B7U }; // 2 / 7 (round) + yield return new object[] { 0x2F945855U, 0x2F9B2071U, 0x2F7270E1U }; // full-precision / full-precision (round) + yield return new object[] { 0x6CB8967FU, 0x2F90F447U, 0x35800009U }; // all-nines / near-one full precision (round) + yield return new object[] { 0x77E95440U, 0x30000001U, 0x78000000U }; // huge / tiny -> +Inf (overflow) + yield return new object[] { 0xF7E95440U, 0x30000001U, 0xF8000000U }; // -huge / tiny -> -Inf (overflow) + yield return new object[] { 0x02800001U, 0x35000001U, 0x00000001U }; // tiny / large -> underflow + yield return new object[] { 0x32800001U, 0x5F000003U, 0x02B2DCD5U }; // 1 / huge -> tiny subnormal (round) + yield return new object[] { 0x03000001U, 0x35000001U, 0x00800001U }; // small / large (subnormal-ish) + yield return new object[] { 0x78000002U, 0x32800001U, 0x78000000U }; // non-canonical +Inf / 1 -> canonical +Inf + yield return new object[] { 0x32800001U, 0xF8000005U, 0x80000000U }; // 1 / non-canonical -Inf -> canonical -0 (Etiny) + yield return new object[] { 0x2F2C8C35U, 0x2A00002DU, 0x36E2FEAFU }; + yield return new object[] { 0xB8002104U, 0xB98043D6U, 0x2DCA43A5U }; + yield return new object[] { 0xA8800010U, 0x34802213U, 0xA21BFCF7U }; + yield return new object[] { 0x318496C5U, 0xA71C20DCU, 0xB998E4C6U }; + yield return new object[] { 0x2E830F1EU, 0xA9000005U, 0xB7861E3CU }; + yield return new object[] { 0xAD00006CU, 0xB7011BA9U, 0x2416B197U }; + yield return new object[] { 0xAC000017U, 0x380B7DD1U, 0xA12E99C7U }; + yield return new object[] { 0xBC800009U, 0xA600170BU, 0x449747B2U }; + yield return new object[] { 0x1C8C3773U, 0x4C800FA6U, 0x009E7EEAU }; + yield return new object[] { 0xB2000007U, 0xEAD57BF3U, 0x336D077CU }; + yield return new object[] { 0xBA000010U, 0x2F0ED152U, 0xB8192411U }; + yield return new object[] { 0x6A41D272U, 0xA8800006U, 0xB315A313U }; + yield return new object[] { 0xAC0000EBU, 0x28800027U, 0xB35BF1A9U }; + yield return new object[] { 0xAB8B81BBU, 0xB0000044U, 0x2D10EBF5U }; + yield return new object[] { 0x390001B3U, 0xB885B1AEU, 0xAE91C985U }; + yield return new object[] { 0x3B800006U, 0x27007EF8U, 0x421C2AA7U }; + yield return new object[] { 0x27034AB4U, 0x3280024CU, 0x2537FBB0U }; + yield return new object[] { 0x3B800094U, 0x3685C05CU, 0x32BBE9F9U }; + yield return new object[] { 0xC0001DD4U, 0x330000DDU, 0xBD34B8E4U }; + yield return new object[] { 0x378000FAU, 0x9A02AE5DU, 0xCB95B5D7U }; + yield return new object[] { 0x278014E4U, 0x3806111AU, 0x1E148643U }; + yield return new object[] { 0xB20ACE79U, 0x3204CA4CU, 0xAFA26C55U }; + yield return new object[] { 0xB98025B8U, 0xB2000002U, 0x3A0012DCU }; + yield return new object[] { 0xB780002BU, 0x3B80007DU, 0xAD000158U }; + yield return new object[] { 0xDA81A30AU, 0x38002124U, 0xD2934B2BU }; + yield return new object[] { 0xB5800001U, 0x36000006U, 0xAE996E6BU }; + yield return new object[] { 0xB8800004U, 0x35297F01U, 0xB0167191U }; + yield return new object[] { 0xAF00252CU, 0xBF000006U, 0x22800632U }; + yield return new object[] { 0x92EA6215U, 0x38800002U, 0x8CB5310AU }; + yield return new object[] { 0xAE800008U, 0xAE00009FU, 0x2F4CC617U }; + yield return new object[] { 0x2A0025A7U, 0x35001CE0U, 0x2493E5A9U }; + yield return new object[] { 0x45800056U, 0x32000030U, 0x431B56B3U }; + yield return new object[] { 0x360DAA53U, 0xB20D6A8EU, 0xB38F8AC7U }; + yield return new object[] { 0xAF800202U, 0xC000857DU, 0x1E16F36FU }; + yield return new object[] { 0xE1000004U, 0x2D00003DU, 0x8914FBCEU }; + yield return new object[] { 0xBF805674U, 0x6E377AE8U, 0xB5220489U }; + yield return new object[] { 0x2E0087F8U, 0x2F0E50D7U, 0x2DB89C87U }; + yield return new object[] { 0x32800061U, 0x4D80016BU, 0x1428C630U }; + yield return new object[] { 0xB1FD9564U, 0x33800008U, 0xB08FB2ACU }; + yield return new object[] { 0xA9000C89U, 0xC0000CD9U, 0x6614E05DU }; + yield return new object[] { 0x37001CE3U, 0x58095C61U, 0x0D9264B8U }; + yield return new object[] { 0x2E8018F2U, 0xB98002B6U, 0xE92C6841U }; + yield return new object[] { 0x400B2B74U, 0xAE0003ACU, 0xC2F6D3B7U }; + yield return new object[] { 0x2B000676U, 0xD1000009U, 0x8A9C0AD2U }; + yield return new object[] { 0xDD8E1313U, 0x33325AAEU, 0xD9AAA65CU }; + yield return new object[] { 0x3500008BU, 0x38800030U, 0x2C2C2FD9U }; + yield return new object[] { 0x338DB8E1U, 0xB9000023U, 0xAC83EBAEU }; + yield return new object[] { 0xB700030FU, 0x2A807402U, 0xBB283AFDU }; + yield return new object[] { 0xA68542F3U, 0x35000005U, 0xA38A85E6U }; + yield return new object[] { 0x261B9CA7U, 0xB7000005U, 0xA18585BBU }; + yield return new object[] { 0x33000007U, 0xB180033BU, 0xEBE127C9U }; + yield return new object[] { 0x0D80010EU, 0xBC000016U, 0x8192BA09U }; + yield return new object[] { 0x310000DCU, 0xAC0583C6U, 0xB2DCE222U }; + yield return new object[] { 0x297CCD10U, 0x2A000006U, 0x3194CCD8U }; + yield return new object[] { 0x15000001U, 0xA7010BBCU, 0x9B16433AU }; + yield return new object[] { 0xAB8002D0U, 0xAA84EC44U, 0x2F220D78U }; + yield return new object[] { 0xB4000180U, 0x38002093U, 0xAAC643C5U }; + yield return new object[] { 0x2A015FAAU, 0x3A000006U, 0x2196E511U }; + yield return new object[] { 0x388002C5U, 0x2A04F49DU, 0x3CA14FCDU }; + yield return new object[] { 0x35003DFEU, 0x35BB999DU, 0x2DBDFF39U }; + } + + [Theory] + [MemberData(nameof(op_Division_TestData))] + public static void op_Division(uint left, uint right, uint expected) + { + Decimal32 result = Unsafe.BitCast(left) / Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Increment_TestData() + { + yield return new object[] { 0xFC000000U, 0xFC000000U }; // NaN + yield return new object[] { 0x78000000U, 0x78000000U }; // +Inf + yield return new object[] { 0xF8000000U, 0xF8000000U }; // -Inf + yield return new object[] { 0x78000002U, 0x78000000U }; // non-canonical +Inf (canonicalizes) + yield return new object[] { 0x32800000U, 0x32800001U }; // +0 -> 1 + yield return new object[] { 0xB2800000U, 0x32800001U }; // -0 -> 1 + yield return new object[] { 0x35000000U, 0x32800001U }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { 0x30000000U, 0x300186A0U }; // 0e-5 + yield return new object[] { 0x32800001U, 0x32800002U }; // 1 -> 2 + yield return new object[] { 0xB2800001U, 0x32800000U }; // -1 -> 0 + yield return new object[] { 0x32800002U, 0x32800003U }; // 2 + yield return new object[] { 0xB2800002U, 0xB2800001U }; // -2 + yield return new object[] { 0x3280000AU, 0x3280000BU }; // 10 + yield return new object[] { 0x32000005U, 0x3200000FU }; // 0.5 -> 1.5 + yield return new object[] { 0xB2000005U, 0x32000005U }; // -0.5 + yield return new object[] { 0x32000019U, 0x32000023U }; // 2.5 + yield return new object[] { 0x32000001U, 0x3200000BU }; // 0.1 -> 1.1 + yield return new object[] { 0x31800177U, 0x318001DBU }; // 3.75 + yield return new object[] { 0xB1800019U, 0x3180004BU }; // -0.25 + yield return new object[] { 0x32800009U, 0x3280000AU }; // 9 -> 10 + yield return new object[] { 0x6CB8967FU, 0x330F4240U }; // all-nines (overflows precision, rounds) + yield return new object[] { 0x328F4240U, 0x328F4241U }; // 10^(P-1) + yield return new object[] { 0x328F423FU, 0x328F4240U }; // (P-1)-nines + yield return new object[] { 0x37000001U, 0x340F4240U }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { 0x77E95440U, 0x77E95440U }; // near-max (1 negligible) + yield return new object[] { 0xF7E95440U, 0xF7E95440U }; // near -max (1 negligible) + yield return new object[] { 0x02800001U, 0x2F8F4240U }; // tiny positive subnormal (++ ~ 1) + yield return new object[] { 0x82800001U, 0x2F8F4240U }; // tiny negative subnormal (++ ~ 1) + yield return new object[] { 0x30000001U, 0x300186A1U }; // 1e-5 + yield return new object[] { 0x2F945855U, 0x2FA39A95U }; // 1.333... full precision + yield return new object[] { 0x6BF8967FU, 0x3010C8E0U }; // 9.999... full precision (carry) + yield return new object[] { 0x2F2C8C35U, 0x2F93B6ACU }; + yield return new object[] { 0x2A00002DU, 0x2F8F4240U }; + yield return new object[] { 0xB8002104U, 0xEDA0F7A0U }; + yield return new object[] { 0xB98043D6U, 0xB89A7F98U }; + yield return new object[] { 0xA8800010U, 0x2F8F4240U }; + yield return new object[] { 0x34802213U, 0x6CC51A38U }; + yield return new object[] { 0x318496C5U, 0x31849729U }; + yield return new object[] { 0xA71C20DCU, 0x2F8F4240U }; + yield return new object[] { 0x2E830F1EU, 0x2F8F4A15U }; + yield return new object[] { 0xA9000005U, 0x2F8F4240U }; + yield return new object[] { 0xAD00006CU, 0x2F8F4240U }; + yield return new object[] { 0xB7011BA9U, 0xB66ECE04U }; + yield return new object[] { 0xAC000017U, 0x2F8F4240U }; + yield return new object[] { 0x380B7DD1U, 0x37F2EA2AU }; + yield return new object[] { 0xBC800009U, 0xEE695440U }; + yield return new object[] { 0xA600170BU, 0x2F8F4240U }; + yield return new object[] { 0x1C8C3773U, 0x2F8F4240U }; + yield return new object[] { 0x4C800FA6U, 0x4B3D2070U }; + yield return new object[] { 0xB2000007U, 0x32000003U }; + yield return new object[] { 0xEAD57BF3U, 0x2F8F4240U }; + yield return new object[] { 0xBA000010U, 0xB7986A00U }; + yield return new object[] { 0x2F0ED152U, 0x2F90BD95U }; + yield return new object[] { 0x6A41D272U, 0x2F8F4240U }; + yield return new object[] { 0xA8800006U, 0x2F8F4240U }; + yield return new object[] { 0xAC0000EBU, 0x2F8F4240U }; + yield return new object[] { 0x28800027U, 0x2F8F4240U }; + yield return new object[] { 0xAB8B81BBU, 0x2F8F4240U }; + yield return new object[] { 0xB0000044U, 0x3001865CU }; + yield return new object[] { 0x390001B3U, 0x37426030U }; + yield return new object[] { 0xB885B1AEU, 0xB838F0CCU }; + yield return new object[] { 0x3B800006U, 0x38DB8D80U }; + yield return new object[] { 0x27007EF8U, 0x2F8F4240U }; + yield return new object[] { 0x27034AB4U, 0x2F8F4240U }; + yield return new object[] { 0x3280024CU, 0x3280024DU }; + yield return new object[] { 0x3B800094U, 0x39969540U }; + yield return new object[] { 0x3685C05CU, 0x36398398U }; + yield return new object[] { 0xC0001DD4U, 0xBEF48420U }; + yield return new object[] { 0x330000DDU, 0x328008A3U }; + yield return new object[] { 0x378000FAU, 0x35A625A0U }; + yield return new object[] { 0x9A02AE5DU, 0x2F8F4240U }; + yield return new object[] { 0x278014E4U, 0x2F8F4240U }; + yield return new object[] { 0x3806111AU, 0x37BCAB04U }; + yield return new object[] { 0xB20ACE79U, 0xB20ACE6FU }; + yield return new object[] { 0x3204CA4CU, 0x3204CA56U }; + yield return new object[] { 0xB98025B8U, 0xEE1356C0U }; + yield return new object[] { 0xB2000002U, 0x32000008U }; + yield return new object[] { 0xB780002BU, 0xB5419CE0U }; + yield return new object[] { 0x3B80007DU, 0x399312D0U }; + yield return new object[] { 0xDA81A30AU, 0xDA105E64U }; + yield return new object[] { 0x38002124U, 0x6DA174A0U }; + yield return new object[] { 0xB5800001U, 0xB28F423FU }; + yield return new object[] { 0x36000006U, 0x335B8D80U }; + yield return new object[] { 0xB8800004U, 0xB5BD0900U }; + yield return new object[] { 0x35297F01U, 0x35297F01U }; + yield return new object[] { 0xAF00252CU, 0x6BD87154U }; + yield return new object[] { 0xBF000006U, 0xBC5B8D80U }; + yield return new object[] { 0x92EA6215U, 0x2F8F4240U }; + yield return new object[] { 0x38800002U, 0x359E8480U }; + yield return new object[] { 0xAE800008U, 0x6BD8967FU }; + yield return new object[] { 0xAE00009FU, 0x6BD8967EU }; + } + + [Theory] + [MemberData(nameof(op_Increment_TestData))] + public static void op_Increment(uint value, uint expected) + { + Decimal32 result = Unsafe.BitCast(value); + result++; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Decrement_TestData() + { + yield return new object[] { 0xFC000000U, 0xFC000000U }; // NaN + yield return new object[] { 0x78000000U, 0x78000000U }; // +Inf + yield return new object[] { 0xF8000000U, 0xF8000000U }; // -Inf + yield return new object[] { 0x78000002U, 0x78000000U }; // non-canonical +Inf (canonicalizes) + yield return new object[] { 0x32800000U, 0xB2800001U }; // +0 -> -1 + yield return new object[] { 0xB2800000U, 0xB2800001U }; // -0 -> -1 + yield return new object[] { 0x35000000U, 0xB2800001U }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { 0x30000000U, 0xB00186A0U }; // 0e-5 + yield return new object[] { 0x32800001U, 0x32800000U }; // 1 -> 0 + yield return new object[] { 0xB2800001U, 0xB2800002U }; // -1 -> -2 + yield return new object[] { 0x32800002U, 0x32800001U }; // 2 + yield return new object[] { 0xB2800002U, 0xB2800003U }; // -2 + yield return new object[] { 0x3280000AU, 0x32800009U }; // 10 + yield return new object[] { 0x32000005U, 0xB2000005U }; // 0.5 -> -0.5 + yield return new object[] { 0xB2000005U, 0xB200000FU }; // -0.5 + yield return new object[] { 0x32000019U, 0x3200000FU }; // 2.5 + yield return new object[] { 0x32000001U, 0xB2000009U }; // 0.1 -> -0.9 + yield return new object[] { 0x31800177U, 0x31800113U }; // 3.75 + yield return new object[] { 0xB1800019U, 0xB180007DU }; // -0.25 + yield return new object[] { 0x32800009U, 0x32800008U }; // 9 -> 8 + yield return new object[] { 0x6CB8967FU, 0x6CB8967EU }; // all-nines (decrement in last place) + yield return new object[] { 0x328F4240U, 0x328F423FU }; // 10^(P-1) + yield return new object[] { 0x328F423FU, 0x328F423EU }; // (P-1)-nines + yield return new object[] { 0x37000001U, 0x340F4240U }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { 0x77E95440U, 0x77E95440U }; // near-max (1 negligible) + yield return new object[] { 0xF7E95440U, 0xF7E95440U }; // near -max (1 negligible) + yield return new object[] { 0x02800001U, 0xAF8F4240U }; // tiny positive subnormal (-- ~ -1) + yield return new object[] { 0x82800001U, 0xAF8F4240U }; // tiny negative subnormal (-- ~ -1) + yield return new object[] { 0x30000001U, 0xB001869FU }; // 1e-5 + yield return new object[] { 0x2F945855U, 0x2F851615U }; // 1.333... full precision + yield return new object[] { 0x6BF8967FU, 0x6BE9543FU }; // 9.999... full precision + yield return new object[] { 0x2F2C8C35U, 0xAF6C0A4BU }; + yield return new object[] { 0x2A00002DU, 0xAF8F4240U }; + yield return new object[] { 0xB8002104U, 0xEDA0F7A0U }; + yield return new object[] { 0xB98043D6U, 0xB89A7F98U }; + yield return new object[] { 0xA8800010U, 0xAF8F4240U }; + yield return new object[] { 0x34802213U, 0x6CC51A38U }; + yield return new object[] { 0x318496C5U, 0x31849661U }; + yield return new object[] { 0xA71C20DCU, 0xAF8F4240U }; + yield return new object[] { 0x2E830F1EU, 0xEBD84830U }; + yield return new object[] { 0xA9000005U, 0xAF8F4240U }; + yield return new object[] { 0xAD00006CU, 0xAF8F4240U }; + yield return new object[] { 0xB7011BA9U, 0xB66ECE04U }; + yield return new object[] { 0xAC000017U, 0xAF8F4240U }; + yield return new object[] { 0x380B7DD1U, 0x37F2EA2AU }; + yield return new object[] { 0xBC800009U, 0xEE695440U }; + yield return new object[] { 0xA600170BU, 0xAF8F4240U }; + yield return new object[] { 0x1C8C3773U, 0xAF8F4240U }; + yield return new object[] { 0x4C800FA6U, 0x4B3D2070U }; + yield return new object[] { 0xB2000007U, 0xB2000011U }; + yield return new object[] { 0xEAD57BF3U, 0xAF8F4240U }; + yield return new object[] { 0xBA000010U, 0xB7986A00U }; + yield return new object[] { 0x2F0ED152U, 0xEBC9C52EU }; + yield return new object[] { 0x6A41D272U, 0xAF8F4240U }; + yield return new object[] { 0xA8800006U, 0xAF8F4240U }; + yield return new object[] { 0xAC0000EBU, 0xAF8F4240U }; + yield return new object[] { 0x28800027U, 0xAF8F4240U }; + yield return new object[] { 0xAB8B81BBU, 0xAF8F4240U }; + yield return new object[] { 0xB0000044U, 0xB00186E4U }; + yield return new object[] { 0x390001B3U, 0x37426030U }; + yield return new object[] { 0xB885B1AEU, 0xB838F0CCU }; + yield return new object[] { 0x3B800006U, 0x38DB8D80U }; + yield return new object[] { 0x27007EF8U, 0xAF8F4240U }; + yield return new object[] { 0x27034AB4U, 0xAF8F4240U }; + yield return new object[] { 0x3280024CU, 0x3280024BU }; + yield return new object[] { 0x3B800094U, 0x39969540U }; + yield return new object[] { 0x3685C05CU, 0x36398398U }; + yield return new object[] { 0xC0001DD4U, 0xBEF48420U }; + yield return new object[] { 0x330000DDU, 0x328008A1U }; + yield return new object[] { 0x378000FAU, 0x35A625A0U }; + yield return new object[] { 0x9A02AE5DU, 0xAF8F4240U }; + yield return new object[] { 0x278014E4U, 0xAF8F4240U }; + yield return new object[] { 0x3806111AU, 0x37BCAB04U }; + yield return new object[] { 0xB20ACE79U, 0xB20ACE83U }; + yield return new object[] { 0x3204CA4CU, 0x3204CA42U }; + yield return new object[] { 0xB98025B8U, 0xEE1356C0U }; + yield return new object[] { 0xB2000002U, 0xB200000CU }; + yield return new object[] { 0xB780002BU, 0xB5419CE0U }; + yield return new object[] { 0x3B80007DU, 0x399312D0U }; + yield return new object[] { 0xDA81A30AU, 0xDA105E64U }; + yield return new object[] { 0x38002124U, 0x6DA174A0U }; + yield return new object[] { 0xB5800001U, 0xB28F4241U }; + yield return new object[] { 0x36000006U, 0x335B8D80U }; + yield return new object[] { 0xB8800004U, 0xB5BD0900U }; + yield return new object[] { 0x35297F01U, 0x35297F01U }; + yield return new object[] { 0xAF00252CU, 0xAF8F45F8U }; + yield return new object[] { 0xBF000006U, 0xBC5B8D80U }; + yield return new object[] { 0x92EA6215U, 0xAF8F4240U }; + yield return new object[] { 0x38800002U, 0x359E8480U }; + yield return new object[] { 0xAE800008U, 0xAF8F4240U }; + yield return new object[] { 0xAE00009FU, 0xAF8F4240U }; + } + + [Theory] + [MemberData(nameof(op_Decrement_TestData))] + public static void op_Decrement(uint value, uint expected) + { + Decimal32 result = Unsafe.BitCast(value); + result--; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() { NumberStyles style = NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters; @@ -569,5 +1528,45 @@ public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, Nu Assert.False(Decimal32.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out int bytesConsumed)); Assert.Equal(0, bytesConsumed); } + + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal32Arithmetic), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Arithmetic_IntelReferenceVectors(string operation, uint left, uint right, uint expected) + { + Decimal32 l = Unsafe.BitCast(left); + Decimal32 r = Unsafe.BitCast(right); + + Decimal32 result = operation switch + { + "add" => l + r, + "sub" => l - r, + "mul" => l * r, + "div" => l / r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [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) + { + Decimal32 l = Unsafe.BitCast(left); + Decimal32 r = Unsafe.BitCast(right); + + bool result = operation switch + { + "quiet_equal" => l == r, + "quiet_not_equal" => l != r, + "quiet_less" => l < r, + "quiet_greater" => l > r, + "quiet_less_equal" => l <= r, + "quiet_greater_equal" => l >= r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, result); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Decimal64Tests.cs index 84b702046c9fcf..c000eca6134046 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,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; @@ -505,6 +505,965 @@ public static IEnumerable NaN_NonCanonicalEncodings64_TestData() yield return new object[] { Decimal64.NaN, canonical | 0x03FF_FFFF_FFFF_FFFFUL }; } + public static IEnumerable Zero_NonCanonicalEncodings_TestData() + { + // A finite encoding whose significand exceeds MaxSignificand (9_999_999_999_999_999) is non-canonical and represents zero. + const ulong PositiveZero = 0x31C0_0000_0000_0000; + const ulong NegativeZero = 0xB1C0_0000_0000_0000; + + yield return new object[] { PositiveZero, 0x6C77_FFFF_FFFF_FFFFUL }; + yield return new object[] { PositiveZero, 0x6C73_86F2_6FC1_0000UL }; + yield return new object[] { NegativeZero, 0xEC77_FFFF_FFFF_FFFFUL }; + yield return new object[] { NegativeZero, 0xEC73_86F2_6FC1_0000UL }; + } + + [Theory] + [MemberData(nameof(Zero_NonCanonicalEncodings_TestData))] + public static void Finite_NonCanonicalEncodings_BehaveAsZero(ulong canonicalZero, ulong encoding) + { + Decimal64 zero = Unsafe.BitCast(canonicalZero); + Decimal64 nc = Unsafe.BitCast(encoding); + Decimal64 one = Unsafe.BitCast(0x31C0_0000_0000_0001UL); + Decimal64 inf = Decimal64.PositiveInfinity; + + // A non-canonical finite encoding compares, hashes, and equals as zero. + Assert.Equal(0, zero.CompareTo(nc)); + Assert.True(zero == nc); + Assert.Equal(zero.GetHashCode(), nc.GetHashCode()); + + // Arithmetic treats the non-canonical operand identically to canonical zero. + Assert.Equal(Bits(zero + one), Bits(nc + one)); + Assert.Equal(Bits(zero - one), Bits(nc - one)); + Assert.Equal(Bits(one + zero), Bits(one + nc)); + Assert.Equal(Bits(zero * one), Bits(nc * one)); + Assert.Equal(Bits(one * zero), Bits(one * nc)); + Assert.Equal(Bits(zero / one), Bits(nc / one)); + + // Division-by-zero and invalid operations must trigger for non-canonical zero. + Assert.Equal(Bits(one / zero), Bits(one / nc)); // finite / 0 -> Infinity + Assert.Equal(Bits(zero / zero), Bits(nc / nc)); // 0 / 0 -> NaN + Assert.Equal(Bits(inf * zero), Bits(inf * nc)); // Infinity * 0 -> NaN + + static ulong Bits(Decimal64 value) => Unsafe.BitCast(value); + } + + public static IEnumerable UnaryNegation_TestData() + { + yield return new object[] { 0x31C0_0000_0000_0000UL, 0xB1C0_0000_0000_0000UL }; // +0 -> -0 + yield return new object[] { 0xB1C0_0000_0000_0000UL, 0x31C0_0000_0000_0000UL }; // -0 -> +0 + yield return new object[] { 0x7800_0000_0000_0000UL, 0xF800_0000_0000_0000UL }; // +Infinity -> -Infinity + yield return new object[] { 0xF800_0000_0000_0000UL, 0x7800_0000_0000_0000UL }; // -Infinity -> +Infinity + yield return new object[] { 0xFC00_0000_0000_0000UL, 0x7C00_0000_0000_0000UL }; // NaN -> sign-flipped NaN + yield return new object[] { 0x7C00_0000_0000_0000UL, 0xFC00_0000_0000_0000UL }; + } + + [Theory] + [MemberData(nameof(UnaryNegation_TestData))] + public static void op_UnaryNegation(ulong value, ulong expected) + { + Decimal64 result = -Unsafe.BitCast(value); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [Theory] + [InlineData("123")] + [InlineData("-123")] + [InlineData("4567.891")] + [InlineData("-4567.891")] + [InlineData("9.999999999999999e384")] + [InlineData("1e-398")] + public static void op_UnaryNegation_FiniteRoundTrips(string value) + { + Decimal64 d = Decimal64.Parse(value, CultureInfo.InvariantCulture); + Decimal64 negated = -d; + + ulong dBits = Unsafe.BitCast(d); + ulong negatedBits = Unsafe.BitCast(negated); + + Assert.Equal(dBits ^ 0x8000_0000_0000_0000UL, negatedBits); // only the sign bit differs + Assert.Equal(dBits, Unsafe.BitCast(-negated)); // double negation is identity + } + + [Theory] + [InlineData(0x31C0_0000_0000_0000UL)] // +0 + [InlineData(0xB1C0_0000_0000_0000UL)] // -0 + [InlineData(0x7800_0000_0000_0000UL)] // +Infinity + [InlineData(0xF800_0000_0000_0000UL)] // -Infinity + [InlineData(0xFC00_0000_0000_0000UL)] // NaN + [InlineData(0x2FE8_6F26_FC0F_FFFFUL)] // arbitrary finite + public static void op_UnaryPlus(ulong value) + { + Decimal64 d = Unsafe.BitCast(value); + Assert.Equal(value, Unsafe.BitCast(+d)); + } + public static IEnumerable op_Addition_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, 0xFC000000_00000000UL }; // NaN + 1 -> NaN + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // 1 + NaN -> NaN + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // +Inf + 1 -> +Inf + yield return new object[] { 0x31C00000_00000001UL, 0x78000000_00000000UL, 0x78000000_00000000UL }; // 1 + +Inf -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0xF8000000_00000000UL, 0x7C000000_00000000UL }; // +Inf + -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000_00000000UL, 0x78000000_00000000UL, 0x7C000000_00000000UL }; // -Inf + +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, 0x78000000_00000000UL }; // +Inf + +Inf -> +Inf + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, 0xF8000000_00000000UL }; // -Inf + -Inf -> -Inf + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000000UL, 0x31C00000_00000000UL }; // +0 + +0 -> +0 + yield return new object[] { 0xB1C00000_00000000UL, 0xB1C00000_00000000UL, 0xB1C00000_00000000UL }; // -0 + -0 -> -0 + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, 0x31C00000_00000000UL }; // +0 + -0 -> +0 (round-half-even) + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000000UL, 0x31C00000_00000000UL }; // -0 + +0 -> +0 + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000000UL, 0x31C00000_00000001UL }; // 1 + 0 -> 1 + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000001UL, 0x31C00000_00000001UL }; // 0 + 1 -> 1 + yield return new object[] { 0x78000000_00000002UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // non-canonical +Inf + 1 -> canonical +Inf + yield return new object[] { 0x31C00000_00000001UL, 0xF8000000_00000005UL, 0xF8000000_00000000UL }; // 1 + non-canonical -Inf -> canonical -Inf + yield return new object[] { 0x78000000_0000000FUL, 0xF8000000_00000003UL, 0x7C000000_00000000UL }; // non-canonical +Inf + non-canonical -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000002UL, 0x31C00000_00000003UL }; // 1 + 2 -> 3 + yield return new object[] { 0x31A00000_0000000FUL, 0x31A00000_00000019UL, 0x31A00000_00000028UL }; // 1.5 + 2.5 -> 4.0 + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_00000002UL, 0x31A00000_00000003UL }; // 0.1 + 0.2 -> 0.3 + yield return new object[] { 0x31C00000_00000001UL, 0xB1C00000_00000001UL, 0x31C00000_00000000UL }; // 1 + -1 -> +0 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000001UL, 0x31C00000_00000000UL }; // -1 + 1 -> +0 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x31C00000_00000001UL, 0x31E38D7E_A4C68000UL }; // all-nines + 1 (carry/overflow to next magnitude) + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFFUL, 0x31E71AFD_498D0000UL }; // big + big (round) + yield return new object[] { 0x31C00000_00000001UL, 0x2FC00000_00000001UL, 0x2FE38D7E_A4C68000UL }; // 1 + 1e-P (alignment beyond precision) + yield return new object[] { 0x31C00000_00000001UL, 0x2F200000_00000001UL, 0x2FE38D7E_A4C68000UL }; // 1 + tiny (sticky rounding) + yield return new object[] { 0x5FE38D7E_A4C68000UL, 0x5FE38D7E_A4C68000UL, 0x5FE71AFD_498D0000UL }; // max-ish + max-ish (overflow to Inf) + yield return new object[] { 0x31000000_0012D687UL, 0x31000000_0074CBB1UL, 0x31000000_0087A238UL }; // cohort / preferred exponent + yield return new object[] { 0x31C00000_00000064UL, 0x31600000_00000001UL, 0x31600000_000186A1UL }; // 100 + 0.001 (exponent spread) + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0xEC7386F2_6FC0FFFEUL, 0x31C00000_00000001UL }; // cancellation leaving small + yield return new object[] { 0xAF200000_000023D2UL, 0xAFA34A58_43C82F35UL, 0xEBE0E772_A5D1D81BUL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0xB2400000_000014EDUL, 0xB0D30829_C20FD000UL }; + yield return new object[] { 0x31200054_42297730UL, 0x2F000063_A23BC84AUL, 0x30ACDB58_73BFC300UL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0x328000E1_F407BFE1UL, 0x6C827A4C_6EB74510UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0x14600009_ED261C68UL, 0x94D63C8D_4F42A1D5UL }; + yield return new object[] { 0xB1E00000_173C0E97UL, 0x17C00575_228BEDE5UL, 0xB10DD951_783BC580UL }; + yield return new object[] { 0x31C00000_3838FC8EUL, 0x33800000_0008C901UL, 0x3254745E_CA115476UL }; + yield return new object[] { 0x33E00000_00000006UL, 0xB3000000_000738AFUL, 0x33000000_038C4E51UL }; + yield return new object[] { 0xB120007E_4A0EE845UL, 0xB1A2CD7D_00E19992UL, 0xB19C06E2_29247E66UL }; + yield return new object[] { 0xB1800000_003142FFUL, 0x2F4004A5_E17CD24DUL, 0xB06B7839_F143220AUL }; + yield return new object[] { 0x30A1AE18_98FE6B6EUL, 0xB2400000_00034D87UL, 0xB107B037_771252EFUL }; + yield return new object[] { 0x1040016E_281289C6UL, 0x2E417F41_004E3595UL, 0x2E2EF88A_030E17D2UL }; + yield return new object[] { 0x2F2160D1_63A6598CUL, 0xAD988DFF_DCC311D5UL, 0x2F0DC82D_E47F6478UL }; + yield return new object[] { 0xAF800000_000149F7UL, 0x82600001_9308E202UL, 0xAE3E0297_BAE1D800UL }; + yield return new object[] { 0xB3600000_0000230CUL, 0xB8600000_00000003UL, 0xB68AA87B_EE538000UL }; + yield return new object[] { 0xAD80816C_363173A8UL, 0x2FE00718_1926DD3EUL, 0x2F9BB622_3FD03A30UL }; + yield return new object[] { 0xB2800000_00000000UL, 0x2F000000_007AF558UL, 0x2F000000_007AF558UL }; + yield return new object[] { 0xAE836513_54C06345UL, 0x8D000000_0000002AUL, 0xEB99F2C1_4F83E0B2UL }; + yield return new object[] { 0xAE543A07_C082FDB3UL, 0x35200000_00000007UL, 0x3358DE76_816D8000UL }; + yield return new object[] { 0x32600000_0000B48AUL, 0x31400000_00015465UL, 0x31402A08_F77A3865UL }; + yield return new object[] { 0x33400000_000E8F6AUL, 0x33C00000_00000287UL, 0x33400000_007148DAUL }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0x31C00000_000001BBUL, 0x302FBD0F_C05A7EC7UL }; + yield return new object[] { 0x31200015_EE481751UL, 0xB06385FA_ED15C9E7UL, 0x6C211C84_0F44FE09UL }; + yield return new object[] { 0xB3A00000_0001B644UL, 0x31400000_88C9241BUL, 0xB263FC6A_AB408FFEUL }; + yield return new object[] { 0xCE000000_2C155ADEUL, 0x2E400000_00000CC1UL, 0xCD3A4698_81BB8300UL }; + yield return new object[] { 0x2E0059A2_1C73F798UL, 0xAEC00000_0000038FUL, 0x2E0059A1_E62735D8UL }; + yield return new object[] { 0xB4400000_00000000UL, 0xB3C00000_00000002UL, 0xB3C00000_00000002UL }; + yield return new object[] { 0x14200000_1E148863UL, 0xAF600000_00000003UL, 0xAD8AA87B_EE538000UL }; + yield return new object[] { 0xB3000000_093826EBUL, 0x27600000_00000008UL, 0xB2257EC2_9E692780UL }; + yield return new object[] { 0xBDA00000_0054E714UL, 0xAEC00000_00083858UL, 0xBC93C497_9C5DC800UL }; + yield return new object[] { 0xBBE00000_00000008UL, 0xB1000000_00000008UL, 0xBA1C6BF5_26340000UL }; + yield return new object[] { 0xD8600000_72FA8C0BUL, 0x30404C87_C53B7F29UL, 0xD7A6DA6F_8B62D8C0UL }; + yield return new object[] { 0x31E00000_00011A74UL, 0x31EB8DA8_BD290BE9UL, 0x31EB8DA8_BD2A265DUL }; + yield return new object[] { 0xAE8000BE_61899933UL, 0x2E8E68EB_22D62BD8UL, 0x2E8E682C_C14C92A5UL }; + yield return new object[] { 0x30A00002_3C81B477UL, 0xB3400000_0000019EUL, 0xB1AEB54E_DD5EBFA0UL }; + yield return new object[] { 0xAFC00016_E7160E75UL, 0x32600000_00000013UL, 0x30A6C00A_39129993UL }; + yield return new object[] { 0x328000C6_0667F8FFUL, 0x437376D5_46AB54AAUL, 0x437376D5_46AB54AAUL }; + yield return new object[] { 0x316008E3_5687A600UL, 0x32C00000_000000BCUL, 0x316019FC_8DDA0600UL }; + yield return new object[] { 0x32600011_10EB5AFDUL, 0xB1200000_70F0F514UL, 0x31DA0A71_1FB6021CUL }; + yield return new object[] { 0x2F600000_0000018DUL, 0x34000000_000001F2UL, 0x3271B148_9AFB4000UL }; + yield return new object[] { 0x3260573E_35DB7C8EUL, 0x328018AA_7862D5CEUL, 0x32614DE6_E9B7D69AUL }; + yield return new object[] { 0x22C00000_00000005UL, 0xB0A00000_000004FDUL, 0xAF24896C_BB60D000UL }; + yield return new object[] { 0xD4200014_54458F18UL, 0xB2C00000_0000001DUL, 0xD39F0516_A377FF00UL }; + yield return new object[] { 0x30200000_0000004CUL, 0x324199F5_8C48AAF6UL, 0x32300397_7AD6AD9CUL }; + yield return new object[] { 0xEB90A2E6_7C00A0EDUL, 0xB2C00000_000020F5UL, 0xB15DF968_23F85000UL }; + yield return new object[] { 0xB282B0EE_E060C24AUL, 0x30600000_00000008UL, 0xB27AE954_C3C796E4UL }; + yield return new object[] { 0x2E20004A_73ED6947UL, 0x9C6F546E_573DB6B7UL, 0x2DAB5C50_69E06570UL }; + yield return new object[] { 0xB1600000_00000054UL, 0x30800000_00000500UL, 0xB0800000_32115D00UL }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0x31800000_00000055UL, 0x2FDE32B4_78974000UL }; + yield return new object[] { 0xB2E00000_00000366UL, 0xB2C00000_00001F1DUL, 0xB2C00000_00004119UL }; + yield return new object[] { 0x33000000_00000002UL, 0x33200000_02DF7470UL, 0x33000000_1CBA8C62UL }; + yield return new object[] { 0xB062FAF8_87469CE0UL, 0x30200000_00000045UL, 0xB05DCDB5_48C220B9UL }; + yield return new object[] { 0xB0200000_000E661FUL, 0xB1000000_00076DFBUL, 0xB020046D_AB3E759FUL }; + yield return new object[] { 0xA9800000_01ECEE93UL, 0x2DC04A74_C2BEB838UL, 0x2D9D159C_127FF5E0UL }; + yield return new object[] { 0x29200001_1579BC2EUL, 0xAFC058BC_B025DCC1UL, 0xEBE2A9B4_CECA3B64UL }; + yield return new object[] { 0xB3C00000_00000012UL, 0xD5600000_0000001EUL, 0xD3AAA87B_EE538000UL }; + yield return new object[] { 0xAF400000_000003E0UL, 0xB2E00000_1892B9E7UL, 0xB20EA590_A3724D80UL }; + yield return new object[] { 0x32400000_1E910857UL, 0xB120000E_041E212AUL, 0x31723815_132D90B0UL }; + yield return new object[] { 0x2FE00000_36F3932DUL, 0x32000000_00003529UL, 0x30A4D5BB_39132B9AUL }; + yield return new object[] { 0x32C00001_B3BF92FFUL, 0x2FC00000_02D2E250UL, 0x3219F8FD_F0BB7DC0UL }; + } + + [Theory] + [MemberData(nameof(op_Addition_TestData))] + public static void op_Addition(ulong left, ulong right, ulong expected) + { + Decimal64 result = Unsafe.BitCast(left) + Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Subtraction_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, 0xFC000000_00000000UL }; // NaN - 1 -> NaN + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // 1 - NaN -> NaN + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // +Inf - 1 -> +Inf + yield return new object[] { 0x31C00000_00000001UL, 0x78000000_00000000UL, 0xF8000000_00000000UL }; // 1 - +Inf -> -Inf + yield return new object[] { 0x78000000_00000000UL, 0xF8000000_00000000UL, 0x78000000_00000000UL }; // +Inf - -Inf -> +Inf + yield return new object[] { 0xF8000000_00000000UL, 0x78000000_00000000UL, 0xF8000000_00000000UL }; // -Inf - +Inf -> -Inf + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, 0x7C000000_00000000UL }; // +Inf - +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, 0x7C000000_00000000UL }; // -Inf - -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000000UL, 0x31C00000_00000000UL }; // +0 - +0 -> +0 + yield return new object[] { 0xB1C00000_00000000UL, 0xB1C00000_00000000UL, 0x31C00000_00000000UL }; // -0 - -0 -> +0 (round-half-even) + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, 0x31C00000_00000000UL }; // +0 - -0 -> +0 + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000000UL, 0xB1C00000_00000000UL }; // -0 - +0 -> -0 + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000000UL, 0x31C00000_00000001UL }; // 1 - 0 -> 1 + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000001UL, 0xB1C00000_00000001UL }; // 0 - 1 -> -1 + yield return new object[] { 0x78000000_00000002UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // non-canonical +Inf - 1 -> canonical +Inf + yield return new object[] { 0x31C00000_00000001UL, 0xF8000000_00000005UL, 0x78000000_00000000UL }; // 1 - non-canonical -Inf -> canonical +Inf + yield return new object[] { 0x78000000_0000000FUL, 0xF8000000_00000003UL, 0x78000000_00000000UL }; // non-canonical +Inf - non-canonical -Inf -> canonical +Inf + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000002UL, 0xB1C00000_00000001UL }; // 1 - 2 -> -1 + yield return new object[] { 0x31A00000_0000000FUL, 0x31A00000_00000019UL, 0xB1A00000_0000000AUL }; // 1.5 - 2.5 -> -1.0 + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_00000002UL, 0xB1A00000_00000001UL }; // 0.1 - 0.2 -> -0.1 + yield return new object[] { 0x31C00000_00000001UL, 0xB1C00000_00000001UL, 0x31C00000_00000002UL }; // 1 - -1 -> 2 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000001UL, 0xB1C00000_00000002UL }; // -1 - 1 -> -2 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x31C00000_00000001UL, 0x6C7386F2_6FC0FFFEUL }; // all-nines - 1 -> 9_999_999_999_999_998 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFFUL, 0x31C00000_00000000UL }; // big - big -> +0 + yield return new object[] { 0x31C00000_00000001UL, 0x2FC00000_00000001UL, 0x6BF386F2_6FC0FFFFUL }; // 1 - 1e-16 -> 0.9999999999999999 (alignment beyond precision) + yield return new object[] { 0x31C00000_00000001UL, 0x2F200000_00000001UL, 0x2FE38D7E_A4C68000UL }; // 1 - 1e-21 -> 1.000000000000000 (sticky rounding) + yield return new object[] { 0x5FE38D7E_A4C68000UL, 0x5FE38D7E_A4C68000UL, 0x5FE00000_00000000UL }; // max-ish - max-ish -> +0 (preferred exponent retained) + yield return new object[] { 0x31000000_0012D687UL, 0x31000000_0074CBB1UL, 0xB1000000_0061F52AUL }; // cohort / preferred exponent + yield return new object[] { 0x31C00000_00000064UL, 0x31600000_00000001UL, 0x31600000_0001869FUL }; // 100 - 0.001 -> 99.999 (exponent spread) + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0xEC7386F2_6FC0FFFEUL, 0x31E71AFD_498D0000UL }; // opposite signs subtract as add: 9_999_999_999_999_999 - -9_999_999_999_999_998 -> 2.0e16 (carry/rounding) + yield return new object[] { 0xAF200000_000023D2UL, 0xAFA34A58_43C82F35UL, 0x6BE0E772_A5D1D809UL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0xB2400000_000014EDUL, 0x30D30829_C20FD000UL }; + yield return new object[] { 0x31200054_42297730UL, 0x2F000063_A23BC84AUL, 0x30ACDB58_73BFC300UL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0x328000E1_F407BFE1UL, 0xEC827A4C_6EB74510UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0x14600009_ED261C68UL, 0x94D63C8D_5457B34BUL }; + yield return new object[] { 0xB1E00000_173C0E97UL, 0x17C00575_228BEDE5UL, 0xB10DD951_783BC580UL }; + yield return new object[] { 0x31C00000_3838FC8EUL, 0x33800000_0008C901UL, 0xB254745E_CA0E738AUL }; + yield return new object[] { 0x33E00000_00000006UL, 0xB3000000_000738AFUL, 0x33000000_039ABFAFUL }; + yield return new object[] { 0xB120007E_4A0EE845UL, 0xB1A2CD7D_00E19992UL, 0x319C06E1_E87B8102UL }; + yield return new object[] { 0xB1800000_003142FFUL, 0x2F4004A5_E17CD24DUL, 0xB06B7839_F14349F6UL }; + yield return new object[] { 0x30A1AE18_98FE6B6EUL, 0xB2400000_00034D87UL, 0x3107B113_ACA02511UL }; + yield return new object[] { 0x1040016E_281289C6UL, 0x2E417F41_004E3595UL, 0xAE2EF88A_030E17D2UL }; + yield return new object[] { 0x2F2160D1_63A6598CUL, 0xAD988DFF_DCC311D5UL, 0x2F0DC82D_E47F9A78UL }; + yield return new object[] { 0xAF800000_000149F7UL, 0x82600001_9308E202UL, 0xAE3E0297_BAE1D800UL }; + yield return new object[] { 0xB3600000_0000230CUL, 0xB8600000_00000003UL, 0x368AA87B_EE538000UL }; + yield return new object[] { 0xAD80816C_363173A8UL, 0x2FE00718_1926DD3EUL, 0xAF9BB622_3FD03A30UL }; + yield return new object[] { 0xB2800000_00000000UL, 0x2F000000_007AF558UL, 0xAF000000_007AF558UL }; + yield return new object[] { 0xAE836513_54C06345UL, 0x8D000000_0000002AUL, 0xEB99F2C1_4F83E0B2UL }; + yield return new object[] { 0xAE543A07_C082FDB3UL, 0x35200000_00000007UL, 0xB358DE76_816D8000UL }; + yield return new object[] { 0x32600000_0000B48AUL, 0x31400000_00015465UL, 0x31402A08_F7778F9BUL }; + yield return new object[] { 0x33400000_000E8F6AUL, 0x33C00000_00000287UL, 0xB3400000_00542A06UL }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0x31C00000_000001BBUL, 0xB02FBD0F_C05B4139UL }; + yield return new object[] { 0x31200015_EE481751UL, 0xB06385FA_ED15C9E7UL, 0x6C21D0E9_71E2F337UL }; + yield return new object[] { 0xB3A00000_0001B644UL, 0x31400000_88C9241BUL, 0xB263FC6A_AB409002UL }; + yield return new object[] { 0xCE000000_2C155ADEUL, 0x2E400000_00000CC1UL, 0xCD3A4698_81BB8300UL }; + yield return new object[] { 0x2E0059A2_1C73F798UL, 0xAEC00000_0000038FUL, 0x2E0059A2_52C0B958UL }; + yield return new object[] { 0xB4400000_00000000UL, 0xB3C00000_00000002UL, 0x33C00000_00000002UL }; + yield return new object[] { 0x14200000_1E148863UL, 0xAF600000_00000003UL, 0x2D8AA87B_EE538000UL }; + yield return new object[] { 0xB3000000_093826EBUL, 0x27600000_00000008UL, 0xB2257EC2_9E692780UL }; + yield return new object[] { 0xBDA00000_0054E714UL, 0xAEC00000_00083858UL, 0xBC93C497_9C5DC800UL }; + yield return new object[] { 0xBBE00000_00000008UL, 0xB1000000_00000008UL, 0xBA1C6BF5_26340000UL }; + yield return new object[] { 0xD8600000_72FA8C0BUL, 0x30404C87_C53B7F29UL, 0xD7A6DA6F_8B62D8C0UL }; + yield return new object[] { 0x31E00000_00011A74UL, 0x31EB8DA8_BD290BE9UL, 0xB1EB8DA8_BD27F175UL }; + yield return new object[] { 0xAE8000BE_61899933UL, 0x2E8E68EB_22D62BD8UL, 0xAE8E69A9_845FC50BUL }; + yield return new object[] { 0x30A00002_3C81B477UL, 0xB3400000_0000019EUL, 0x31AEB54E_DD5EC060UL }; + yield return new object[] { 0xAFC00016_E7160E75UL, 0x32600000_00000013UL, 0xB0A6C00A_3912E66DUL }; + yield return new object[] { 0x328000C6_0667F8FFUL, 0x437376D5_46AB54AAUL, 0xC37376D5_46AB54AAUL }; + yield return new object[] { 0x316008E3_5687A600UL, 0x32C00000_000000BCUL, 0xB1600835_E0CABA00UL }; + yield return new object[] { 0x32600011_10EB5AFDUL, 0xB1200000_70F0F514UL, 0x31DA0A71_1FB69624UL }; + yield return new object[] { 0x2F600000_0000018DUL, 0x34000000_000001F2UL, 0xB271B148_9AFB4000UL }; + yield return new object[] { 0x3260573E_35DB7C8EUL, 0x328018AA_7862D5CEUL, 0xB2609F6A_7E00DD7EUL }; + yield return new object[] { 0x22C00000_00000005UL, 0xB0A00000_000004FDUL, 0x2F24896C_BB60D000UL }; + yield return new object[] { 0xD4200014_54458F18UL, 0xB2C00000_0000001DUL, 0xD39F0516_A377FF00UL }; + yield return new object[] { 0x30200000_0000004CUL, 0x324199F5_8C48AAF6UL, 0xB2300397_7AD6AD9CUL }; + yield return new object[] { 0xEB90A2E6_7C00A0EDUL, 0xB2C00000_000020F5UL, 0x315DF968_23F85000UL }; + yield return new object[] { 0xB282B0EE_E060C24AUL, 0x30600000_00000008UL, 0xB27AE954_C3C796E4UL }; + yield return new object[] { 0x2E20004A_73ED6947UL, 0x9C6F546E_573DB6B7UL, 0x2DAB5C50_69E06570UL }; + yield return new object[] { 0xB1600000_00000054UL, 0x30800000_00000500UL, 0xB0800000_32116700UL }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0x31800000_00000055UL, 0xAFDE32B4_78974000UL }; + yield return new object[] { 0xB2E00000_00000366UL, 0xB2C00000_00001F1DUL, 0xB2C00000_000002DFUL }; + yield return new object[] { 0x33000000_00000002UL, 0x33200000_02DF7470UL, 0xB3000000_1CBA8C5EUL }; + yield return new object[] { 0xB062FAF8_87469CE0UL, 0x30200000_00000045UL, 0xB05DCDB5_48C220C7UL }; + yield return new object[] { 0xB0200000_000E661FUL, 0xB1000000_00076DFBUL, 0x3020046D_AB21A961UL }; + yield return new object[] { 0xA9800000_01ECEE93UL, 0x2DC04A74_C2BEB838UL, 0xAD9D159C_127FF5E0UL }; + yield return new object[] { 0x29200001_1579BC2EUL, 0xAFC058BC_B025DCC1UL, 0x6BE2A9B4_CECA3B64UL }; + yield return new object[] { 0xB3C00000_00000012UL, 0xD5600000_0000001EUL, 0x53AAA87B_EE538000UL }; + yield return new object[] { 0xAF400000_000003E0UL, 0xB2E00000_1892B9E7UL, 0x320EA590_A3724D80UL }; + yield return new object[] { 0x32400000_1E910857UL, 0xB120000E_041E212AUL, 0x31723815_5AF0BA50UL }; + yield return new object[] { 0x2FE00000_36F3932DUL, 0x32000000_00003529UL, 0xB0A4D5BB_39132466UL }; + yield return new object[] { 0x32C00001_B3BF92FFUL, 0x2FC00000_02D2E250UL, 0x3219F8FD_F0BB7DC0UL }; + } + + [Theory] + [MemberData(nameof(op_Subtraction_TestData))] + public static void op_Subtraction(ulong left, ulong right, ulong expected) + { + Decimal64 result = Unsafe.BitCast(left) - Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Equality_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0xFC000000_00000000UL, false }; // NaN == NaN -> false + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, false }; // NaN == 1 -> false + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, false }; // 1 == NaN -> false + yield return new object[] { 0xFC000000_00000000UL, 0x78000000_00000000UL, false }; // NaN == +Inf -> false + yield return new object[] { 0xFE000000_00000000UL, 0xFE000000_00000000UL, false }; // sNaN == sNaN -> false + yield return new object[] { 0xFE000000_00000000UL, 0x31C00000_00000001UL, false }; // sNaN == 1 -> false + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, true }; // +Inf == +Inf -> true + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, true }; // -Inf == -Inf -> true + yield return new object[] { 0x78000000_00000000UL, 0xF8000000_00000000UL, false }; // +Inf == -Inf -> false + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, false }; // +Inf == 1 -> false + yield return new object[] { 0x78000000_00000002UL, 0x78000000_00000000UL, true }; // non-canonical +Inf == +Inf -> true + yield return new object[] { 0xF8000000_00000005UL, 0xF8000000_00000000UL, true }; // non-canonical -Inf == -Inf -> true + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, true }; // +0 == -0 -> true + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000000UL, true }; // -0 == +0 -> true + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000000UL, true }; // +0 == +0 -> true + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000001UL, true }; // 1 == 1 -> true + yield return new object[] { 0x31C00000_00000001UL, 0xB1C00000_00000001UL, false }; // 1 == -1 -> false + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000001UL, false }; // -1 == 1 -> false + yield return new object[] { 0x31C00000_00000001UL, 0x31A00000_0000000AUL, true }; // 1 == 1.0 -> true (cohort) + yield return new object[] { 0x31A00000_0000000AUL, 0x31C00000_00000001UL, true }; // 1.0 == 1 -> true (cohort) + yield return new object[] { 0x31800000_00000064UL, 0x31A00000_0000000AUL, true }; // 1.00 == 1.0 -> true (cohort) + yield return new object[] { 0x31C00000_00000064UL, 0x32000000_00000001UL, true }; // 100 == 1e2 -> true (cohort) + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_00000001UL, false }; // 10 == 1 -> false + yield return new object[] { 0x5FE38D7E_A4C68000UL, 0x5FE38D7E_A4C68000UL, true }; // large == large -> true + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFFUL, true }; // all-nines == all-nines -> true + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFEUL, false }; // all-nines == near -> false + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_00000001UL, true }; // 0.1 == 0.1 -> true + yield return new object[] { 0xB1A00000_00000001UL, 0x31A00000_00000001UL, false }; // -0.1 == 0.1 -> false + yield return new object[] { 0x32600000_00000000UL, 0x31C00000_00000000UL, true }; // +0 (exp 5) == +0 -> true (zero cohort) + yield return new object[] { 0xB1A008C2_84977883UL, 0x2E800000_0088FC67UL, false }; + yield return new object[] { 0x32E00000_001FC878UL, 0x32C00000_013DD4B0UL, true }; + yield return new object[] { 0x2EA0391E_4F49DE8FUL, 0x9F400000_00001DF5UL, false }; + yield return new object[] { 0xAEC00000_001D0824UL, 0xAE800000_0B572E10UL, true }; + yield return new object[] { 0xAF800000_2C48DC3AUL, 0xAF600001_BAD89A44UL, true }; + yield return new object[] { 0xB2A00000_001E3059UL, 0xB2600000_0BCAE2C4UL, true }; + yield return new object[] { 0x33600000_00000006UL, 0x33200000_00000258UL, true }; + yield return new object[] { 0xB0600000_00010528UL, 0xB0400000_000A3390UL, true }; + yield return new object[] { 0x31A00000_000003D9UL, 0x31400000_000F07A8UL, true }; + yield return new object[] { 0xB1000000_000014DFUL, 0xA2A00000_000493D9UL, false }; + yield return new object[] { 0xB1A2045D_952633E9UL, 0xB1942BA7_D37E071AUL, true }; + yield return new object[] { 0x18C00000_0000AEAEUL, 0x31C00000_3838FC8EUL, false }; + yield return new object[] { 0x33800000_0008C901UL, 0x33E00000_00000006UL, false }; + yield return new object[] { 0xB3000000_000738AFUL, 0xB2C00000_02D2245CUL, true }; + yield return new object[] { 0xB1C03D4D_B6F5E4F8UL, 0xB197F25B_780D70E0UL, true }; + yield return new object[] { 0x30800000_000003D6UL, 0x2F400664_5DF1A27CUL, false }; + yield return new object[] { 0x30A1AE18_98FE6B6EUL, 0xB2400000_00034D87UL, false }; + yield return new object[] { 0x1040016E_281289C6UL, 0x30000000_00000005UL, false }; + yield return new object[] { 0x2F2160D1_63A6598CUL, 0x2F0DC82D_E47F7F78UL, true }; + yield return new object[] { 0x30E00000_00000C4AUL, 0xB2200000_000025CFUL, false }; + yield return new object[] { 0xB2203A12_8BBE5CE9UL, 0xB20244B9_756FA11AUL, true }; + yield return new object[] { 0xB0E00013_1E45601FUL, 0xB0C000BF_2EB5C136UL, true }; + yield return new object[] { 0x2FC00001_6D25307DUL, 0x34000000_00000007UL, false }; + yield return new object[] { 0x2E406792_5AAB2BB2UL, 0x30604337_B2B054C2UL, false }; + yield return new object[] { 0x6B7B4EB4_CA653CC8UL, 0x31600000_0000DC60UL, false }; + yield return new object[] { 0xB3400000_00000160UL, 0x2DC77033_1BF19A20UL, false }; + yield return new object[] { 0xCC001DE4_66BAF9CBUL, 0xAF600000_0023E448UL, false }; + yield return new object[] { 0x2FA00000_00000008UL, 0x33C00000_00000287UL, false }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0xAF604373_91C90C36UL, false }; + yield return new object[] { 0xB3C00000_00000127UL, 0xA21F2123_A600AF10UL, false }; + yield return new object[] { 0xAEC369E6_4F6A2F25UL, 0xEBAA22FF_1A25D772UL, true }; + yield return new object[] { 0x2F800000_00004BEFUL, 0x2F400000_001DA95CUL, true }; + yield return new object[] { 0x21400000_000001A3UL, 0x21200000_0000105EUL, true }; + yield return new object[] { 0x2EC00000_000E96B9UL, 0x2E600000_38FCC2A8UL, true }; + yield return new object[] { 0xB22014D6_070C597FUL, 0x3169A927_D9913FE9UL, false }; + yield return new object[] { 0xAF600000_00000003UL, 0xAF000000_00000BB8UL, true }; + yield return new object[] { 0x30400011_8446412EUL, 0xBDA00000_0054E714UL, false }; + yield return new object[] { 0xAEC00000_00083858UL, 0xAEA00000_00523370UL, true }; + yield return new object[] { 0xC5E3830C_56EBAA74UL, 0xF1731E7B_6534A888UL, true }; + yield return new object[] { 0xD8600000_72FA8C0BUL, 0xD8400004_7DC9786EUL, true }; + yield return new object[] { 0xB1D51B94_C03688D2UL, 0xB0000000_0007D313UL, false }; + yield return new object[] { 0xB1C00000_000002AAUL, 0x2FA00000_05391305UL, false }; + yield return new object[] { 0xB0000000_038654B6UL, 0xAFE00000_233F4F1CUL, true }; + yield return new object[] { 0x2EE00001_EA1CAC6FUL, 0x2E80077A_80019198UL, true }; + yield return new object[] { 0x5660DA0F_B478C74AUL, 0x5648849D_0CB7C8E4UL, true }; + yield return new object[] { 0xAFC00000_0000DB6BUL, 0xAF600000_035919F8UL, true }; + yield return new object[] { 0x2F000000_000001EFUL, 0x2EA00000_00078D98UL, true }; + yield return new object[] { 0x2E62F469_D9824BA0UL, 0x4DF4BD37_15B124F2UL, false }; + yield return new object[] { 0xB1000000_00014173UL, 0xB0A00000_04E7A938UL, true }; + yield return new object[] { 0xAE6004BD_94F49D11UL, 0xAE402F67_D18E22AAUL, true }; + yield return new object[] { 0x33600000_00B80D1DUL, 0x3260573E_35DB7C8EUL, false }; + yield return new object[] { 0x328018AA_7862D5CEUL, 0x22C00000_00000005UL, false }; + yield return new object[] { 0xB0A00000_000004FDUL, 0xB0400000_00137C48UL, true }; + yield return new object[] { 0x31C00000_000BC1FDUL, 0x31800000_0497C6D4UL, true }; + yield return new object[] { 0x58C11505_F3B6E6F2UL, 0x324199F5_8C48AAF6UL, false }; + yield return new object[] { 0xEB90A2E6_7C00A0EDUL, 0xB27AFF08_C857C807UL, false }; + yield return new object[] { 0x30600000_00000008UL, 0x30200000_00000320UL, true }; + yield return new object[] { 0xB2E00000_00000057UL, 0xB2800000_000153D8UL, true }; + yield return new object[] { 0xB1600000_05307358UL, 0x1CA00000_3594B625UL, false }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0xAA229B20_CA8B0F6EUL, true }; + } + + [Theory] + [MemberData(nameof(op_Equality_TestData))] + public static void op_Equality(ulong left, ulong right, bool expected) + { + Decimal64 l = Unsafe.BitCast(left); + Decimal64 r = Unsafe.BitCast(right); + Assert.Equal(expected, l == r); + Assert.Equal(!expected, l != r); + } + + public static IEnumerable op_Comparison_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, false, false, false, false }; // NaN vs 1 -> unordered + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, false, false, false, false }; // 1 vs NaN -> unordered + yield return new object[] { 0xFC000000_00000000UL, 0xFC000000_00000000UL, false, false, false, false }; // NaN vs NaN -> unordered + yield return new object[] { 0xFE000000_00000000UL, 0x31C00000_00000001UL, false, false, false, false }; // sNaN vs 1 -> unordered + yield return new object[] { 0xFC000000_00000000UL, 0x78000000_00000000UL, false, false, false, false }; // NaN vs +Inf -> unordered + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, false, true, false, true }; // +Inf vs 1 + yield return new object[] { 0x31C00000_00000001UL, 0x78000000_00000000UL, true, false, true, false }; // 1 vs +Inf + yield return new object[] { 0xF8000000_00000000UL, 0x31C00000_00000001UL, true, false, true, false }; // -Inf vs 1 + yield return new object[] { 0x31C00000_00000001UL, 0xF8000000_00000000UL, false, true, false, true }; // 1 vs -Inf + yield return new object[] { 0xF8000000_00000000UL, 0x78000000_00000000UL, true, false, true, false }; // -Inf vs +Inf + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, false, false, true, true }; // +Inf vs +Inf (equal) + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, false, false, true, true }; // -Inf vs -Inf (equal) + yield return new object[] { 0x78000000_00000002UL, 0x78000000_00000000UL, false, false, true, true }; // non-canonical +Inf vs +Inf (equal) + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, false, false, true, true }; // +0 vs -0 (equal) + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000000UL, false, false, true, true }; // -0 vs +0 (equal) + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000001UL, true, false, true, false }; // 0 vs 1 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000000UL, true, false, true, false }; // -1 vs 0 + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000002UL, true, false, true, false }; // 1 vs 2 + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000001UL, false, true, false, true }; // 2 vs 1 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000001UL, true, false, true, false }; // -1 vs 1 + yield return new object[] { 0x31C00000_00000001UL, 0xB1C00000_00000001UL, false, true, false, true }; // 1 vs -1 + yield return new object[] { 0xB1C00000_00000001UL, 0xB1C00000_00000002UL, false, true, false, true }; // -1 vs -2 + yield return new object[] { 0x31C00000_00000001UL, 0x31A00000_0000000AUL, false, false, true, true }; // 1 vs 1.0 (cohort equal) + yield return new object[] { 0x31A00000_0000000AUL, 0x31C00000_00000001UL, false, false, true, true }; // 1.0 vs 1 (cohort equal) + yield return new object[] { 0x31C00000_00000064UL, 0x32000000_00000001UL, false, false, true, true }; // 100 vs 1e2 (cohort equal) + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_00000001UL, false, true, false, true }; // 10 vs 1 + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_00000002UL, true, false, true, false }; // 0.1 vs 0.2 + yield return new object[] { 0x31000000_0098967FUL, 0x31000000_0098967EUL, false, true, false, true }; // close values + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFEUL, false, true, false, true }; // all-nines vs near + yield return new object[] { 0x5FE38D7E_A4C68000UL, 0x5FE05AF3_107A4000UL, false, true, false, true }; // large magnitudes + yield return new object[] { 0xDFE38D7E_A4C68000UL, 0x5FE38D7E_A4C68000UL, true, false, true, false }; // -large vs +large + yield return new object[] { 0xB2400000_000014EDUL, 0x31C00000_002A5135UL, true, false, true, false }; + yield return new object[] { 0x2F000063_A23BC84AUL, 0x2E60007D_E4F31691UL, false, true, false, true }; + yield return new object[] { 0x328000E1_F407BFE1UL, 0xB1000000_000014DFUL, false, true, false, true }; + yield return new object[] { 0xA2200001_2C5C9406UL, 0xB1A2045D_952633E9UL, false, true, false, true }; + yield return new object[] { 0x2F600004_B07A97FAUL, 0x31C00000_3838FC8EUL, true, false, true, false }; + yield return new object[] { 0x33800000_0008C901UL, 0x33E00000_00000006UL, false, true, false, true }; + yield return new object[] { 0xB3000000_000738AFUL, 0xEC2B57F6_AEDFA2F6UL, true, false, true, false }; + yield return new object[] { 0xB1A2CD7D_00E19992UL, 0xB1800000_003142FFUL, true, false, true, false }; + yield return new object[] { 0x2F4004A5_E17CD24DUL, 0x30A1AE18_98FE6B6EUL, true, false, true, false }; + yield return new object[] { 0xB2400000_00034D87UL, 0xB0A00000_000000C7UL, true, false, true, false }; + yield return new object[] { 0x30000000_00000005UL, 0x2F2160D1_63A6598CUL, true, false, true, false }; + yield return new object[] { 0xAD988DFF_DCC311D5UL, 0xB0000691_D863649AUL, false, true, false, true }; + yield return new object[] { 0x30627FCF_B909892FUL, 0x3058FE1D_3A5F5BD6UL, false, false, true, true }; + yield return new object[] { 0xB3600000_0000230CUL, 0xB3400000_00015E78UL, false, false, true, true }; + yield return new object[] { 0x2FC00001_6D25307DUL, 0x34000000_00000007UL, true, false, true, false }; + yield return new object[] { 0x2E406792_5AAB2BB2UL, 0x30604337_B2B054C2UL, true, false, true, false }; + yield return new object[] { 0x6B7B4EB4_CA653CC8UL, 0xAF400000_0000008FUL, false, true, false, true }; + yield return new object[] { 0xB3C00000_00000001UL, 0x2DC77033_1BF19A20UL, true, false, true, false }; + yield return new object[] { 0xCC001DE4_66BAF9CBUL, 0xAF600000_0023E448UL, true, false, true, false }; + yield return new object[] { 0x2FA00000_00000008UL, 0x33C00000_00000287UL, true, false, true, false }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0xAF604373_91C90C36UL, false, true, false, true }; + yield return new object[] { 0xB3C00000_00000127UL, 0xA21F2123_A600AF10UL, true, false, true, false }; + yield return new object[] { 0xAEC369E6_4F6A2F25UL, 0xCE000000_2C155ADEUL, false, true, false, true }; + yield return new object[] { 0x2E400000_00000CC1UL, 0x2EE00000_006399AAUL, true, false, true, false }; + yield return new object[] { 0xAE2000E8_7DB2F031UL, 0xB22014D6_070C597FUL, false, true, false, true }; + yield return new object[] { 0x14200000_1E148863UL, 0x13E0000B_C00546ACUL, false, false, true, true }; + yield return new object[] { 0xB3E00000_00000004UL, 0xB3800000_00000FA0UL, false, false, true, true }; + yield return new object[] { 0x30600000_00000192UL, 0x2DB14774_E29D36EFUL, false, true, false, true }; + yield return new object[] { 0xBA404693_038D50F9UL, 0xB040009D_38654929UL, true, false, true, false }; + yield return new object[] { 0xB4000000_00000009UL, 0x30C00000_000001EBUL, true, false, true, false }; + yield return new object[] { 0x32800029_6783D33DUL, 0xB1C00000_000002AAUL, false, true, false, true }; + yield return new object[] { 0x2FA00000_05391305UL, 0x2F400014_66F24B88UL, false, false, true, true }; + yield return new object[] { 0xB3800000_00006C63UL, 0xB3600000_00043BDEUL, false, false, true, true }; + yield return new object[] { 0x2EE00001_EA1CAC6FUL, 0x5660DA0F_B478C74AUL, true, false, true, false }; + yield return new object[] { 0xB7C00000_03E9AF8BUL, 0x328000C6_0667F8FFUL, true, false, true, false }; + yield return new object[] { 0x437376D5_46AB54AAUL, 0x2E62F469_D9824BA0UL, false, true, false, true }; + yield return new object[] { 0x09200001_FEBFFAC0UL, 0xB1200000_70F0F514UL, false, true, false, true }; + yield return new object[] { 0x2F600000_0000018DUL, 0x2F200000_00009B14UL, false, false, true, true }; + yield return new object[] { 0x31C00000_0093B3B1UL, 0x32200000_03650DEAUL, true, false, true, false }; + yield return new object[] { 0x32C00000_0072C76FUL, 0x22C00000_00000005UL, false, true, false, true }; + yield return new object[] { 0xB0A00000_000004FDUL, 0x31C00000_000BC1FDUL, true, false, true, false }; + yield return new object[] { 0xB2000000_03255E52UL, 0xB1A0000C_49F87050UL, false, false, true, true }; + yield return new object[] { 0xB2600000_00012FDAUL, 0x31200000_007A3E03UL, true, false, true, false }; + yield return new object[] { 0xB18041EB_25020508UL, 0x2FE00040_A971BF9CUL, true, false, true, false }; + yield return new object[] { 0x33800000_0000B296UL, 0x30E00000_0000034FUL, false, true, false, true }; + yield return new object[] { 0xB1600000_05307358UL, 0x1CA00000_3594B625UL, true, false, true, false }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0xAA229B20_CA8B0F6EUL, false, false, true, true }; + yield return new object[] { 0xB3000000_38B20AF3UL, 0xAE800003_9C24FB35UL, true, false, true, false }; + yield return new object[] { 0xB0A00000_2F4E733BUL, 0x31800000_00013536UL, true, false, true, false }; + yield return new object[] { 0xB062FAF8_87469CE0UL, 0x30200000_00000045UL, true, false, true, false }; + yield return new object[] { 0xB0200000_000E661FUL, 0xAFE3054C_CC540881UL, false, true, false, true }; + yield return new object[] { 0xA9800000_01ECEE93UL, 0x25200000_0412E566UL, true, false, true, false }; + yield return new object[] { 0xAF800002_24F7B6CAUL, 0x2E800000_2F26BC28UL, true, false, true, false }; + yield return new object[] { 0x18000001_F084CCA2UL, 0xADE00A2A_BF8CFA22UL, false, true, false, true }; + yield return new object[] { 0xB2E00000_1892B9E7UL, 0x31726749_CD4380CAUL, true, false, true, false }; + yield return new object[] { 0xB120000E_041E212AUL, 0x34000000_00000336UL, true, false, true, false }; + yield return new object[] { 0x32000000_00003529UL, 0x32A00000_25E00AC4UL, true, false, true, false }; + yield return new object[] { 0x2FC00000_02D2E250UL, 0x30A00000_00000001UL, false, true, false, true }; + yield return new object[] { 0xB0200000_045F9E3BUL, 0xC8A00002_48B351F0UL, false, true, false, true }; + yield return new object[] { 0xAE305989_14E871DFUL, 0xB1400000_00000035UL, false, true, false, true }; + } + + [Theory] + [MemberData(nameof(op_Comparison_TestData))] + public static void op_Comparison(ulong left, ulong right, bool lessThan, bool greaterThan, bool lessThanOrEqual, bool greaterThanOrEqual) + { + Decimal64 l = Unsafe.BitCast(left); + Decimal64 r = Unsafe.BitCast(right); + Assert.Equal(lessThan, l < r); + Assert.Equal(greaterThan, l > r); + Assert.Equal(lessThanOrEqual, l <= r); + Assert.Equal(greaterThanOrEqual, l >= r); + } + + public static IEnumerable op_Multiply_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, 0xFC000000_00000000UL }; // NaN * 1 -> NaN + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // 1 * NaN -> NaN + yield return new object[] { 0xFC000000_00000000UL, 0x78000000_00000000UL, 0xFC000000_00000000UL }; // NaN * +Inf -> NaN + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000000UL, 0x7C000000_00000000UL }; // +Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x31C00000_00000000UL, 0x78000000_00000000UL, 0x7C000000_00000000UL }; // +0 * +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000_00000000UL, 0x31C00000_00000000UL, 0x7C000000_00000000UL }; // -Inf * +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000_00000000UL, 0xB1C00000_00000000UL, 0x7C000000_00000000UL }; // +Inf * -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // +Inf * 1 -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0xB1C00000_00000001UL, 0xF8000000_00000000UL }; // +Inf * -1 -> -Inf + yield return new object[] { 0xF8000000_00000000UL, 0x31C00000_00000002UL, 0xF8000000_00000000UL }; // -Inf * 2 -> -Inf + yield return new object[] { 0xF8000000_00000000UL, 0xB1C00000_00000002UL, 0x78000000_00000000UL }; // -Inf * -2 -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, 0x78000000_00000000UL }; // +Inf * +Inf -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0xF8000000_00000000UL, 0xF8000000_00000000UL }; // +Inf * -Inf -> -Inf + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, 0x78000000_00000000UL }; // -Inf * -Inf -> +Inf + yield return new object[] { 0x78000000_00000002UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // non-canonical +Inf * 1 -> canonical +Inf + yield return new object[] { 0x31C00000_00000001UL, 0xF8000000_00000005UL, 0xF8000000_00000000UL }; // 1 * non-canonical -Inf -> canonical -Inf + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000000UL, 0x31C00000_00000000UL }; // +0 * +0 -> +0 + yield return new object[] { 0xB1C00000_00000000UL, 0xB1C00000_00000000UL, 0x31C00000_00000000UL }; // -0 * -0 -> +0 + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, 0xB1C00000_00000000UL }; // +0 * -0 -> -0 + yield return new object[] { 0x31C00000_00000001UL, 0xB1C00000_00000000UL, 0xB1C00000_00000000UL }; // 1 * -0 -> -0 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000000UL, 0xB1C00000_00000000UL }; // -1 * +0 -> -0 + yield return new object[] { 0x32600000_00000000UL, 0x32200000_00000000UL, 0x32C00000_00000000UL }; // 0e5 * 0e3 -> 0e8 (preferred exp) + yield return new object[] { 0x31200000_00000000UL, 0x31C00000_0000000CUL, 0x31200000_00000000UL }; // 0e-5 * 12 -> 0e-5 (preferred exp) + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000005UL, 0x31C00000_0000000AUL }; // 2 * 5 -> 10 (exp 0, trailing zero retained) + yield return new object[] { 0x31C00000_00000002UL, 0x31A00000_00000005UL, 0x31A00000_0000000AUL }; // 2 * 0.5 -> 1.0 + yield return new object[] { 0x31A00000_0000000FUL, 0x31C00000_00000002UL, 0x31A00000_0000001EUL }; // 1.5 * 2 -> 3.0 + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_00000001UL, 0x31800000_00000001UL }; // 0.1 * 0.1 -> 0.01 + yield return new object[] { 0x31A00000_0000000CUL, 0x31A00000_0000000CUL, 0x31800000_00000090UL }; // 1.2 * 1.2 -> 1.44 + yield return new object[] { 0xB1C00000_00000003UL, 0x31C00000_00000007UL, 0xB1C00000_00000015UL }; // -3 * 7 -> -21 + yield return new object[] { 0x31C00000_0000000CUL, 0x31C00000_0000000CUL, 0x31C00000_00000090UL }; // 12 * 12 -> 144 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFFUL, 0x6CF386F2_6FC0FFFEUL }; // all-nines squared (round) + yield return new object[] { 0x2FE4BCA8_DBB35555UL, 0x2FE650E1_24EF1C71UL, 0x2FE86BD6_DBE97B41UL }; // full-precision * full-precision (round) + yield return new object[] { 0x31C38D7E_A4C68000UL, 0x31C38D7E_A4C68000UL, 0x33A38D7E_A4C68000UL }; // 10^(P-1) * 10^(P-1) (trailing zeros beyond precision) + yield return new object[] { 0x5FFFF973_CAFA8000UL, 0x5FFFF973_CAFA8000UL, 0x78000000_00000000UL }; // huge * huge -> +Inf (overflow) + yield return new object[] { 0xDFFFF973_CAFA8000UL, 0x5FFFF973_CAFA8000UL, 0xF8000000_00000000UL }; // -huge * huge -> -Inf (overflow) + yield return new object[] { 0x01C00000_00000001UL, 0x01C00000_00000001UL, 0x00000000_00000000UL }; // tiny * tiny -> underflow + yield return new object[] { 0x01E00000_00000001UL, 0x31200000_00000001UL, 0x01400000_00000001UL }; // small * small (subnormal-ish) + yield return new object[] { 0x662B86F2_6FC0FFFFUL, 0x662B86F2_6FC0FFFFUL, 0x606386F2_6FC0FFFEUL }; // wide product just below min quantum (normal after single rounding) + yield return new object[] { 0x65EB86F2_6FC0FFFFUL, 0x65F386F2_6FC0FFFFUL, 0x00000918_4E72A000UL }; // wide product deep in the subnormal range + yield return new object[] { 0x607386F2_6FC0FFFFUL, 0x607386F2_6FC0FFFFUL, 0x00000000_00000000UL }; // wide product underflow to zero/epsilon + yield return new object[] { 0x5FE00000_00000000UL, 0x5FE00000_00000005UL, 0x5FE00000_00000000UL }; // zero * finite, preferred exponent far above max quantum (clamped) + yield return new object[] { 0xDFE00000_00000000UL, 0x5FE00000_00000005UL, 0xDFE00000_00000000UL }; // -zero * finite, preferred exponent clamped (sign preserved) + yield return new object[] { 0x31E00000_00000000UL, 0x5FE00000_00000005UL, 0x5FE00000_00000000UL }; // zero * finite, preferred exponent one above max quantum (clamped) + yield return new object[] { 0x31C00000_00000000UL, 0x5FE00000_00000005UL, 0x5FE00000_00000000UL }; // zero * finite, preferred exponent exactly at max quantum (no clamp) + yield return new object[] { 0xAF200000_000023D2UL, 0xAFA34A58_43C82F35UL, 0x2D7E2C4D_3A5C54BAUL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0xB2400000_000014EDUL, 0x9A400000_8C401028UL }; + yield return new object[] { 0x31200054_42297730UL, 0x2F000063_A23BC84AUL, 0x2F658071_C3516735UL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0x328000E1_F407BFE1UL, 0x3032A47C_68E29A50UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0x14600009_ED261C68UL, 0x80000000_00000000UL }; + yield return new object[] { 0xB1E00000_173C0E97UL, 0x17C00575_228BEDE5UL, 0x98A84F6B_C6DCC819UL }; + yield return new object[] { 0x31C00000_3838FC8EUL, 0x33800000_0008C901UL, 0x3381EDEC_DAF47A8EUL }; + yield return new object[] { 0x33E00000_00000006UL, 0xB3000000_000738AFUL, 0xB5200000_002B541AUL }; + yield return new object[] { 0xB120007E_4A0EE845UL, 0xB1A2CD7D_00E19992UL, 0x326F33B7_70DDF2ECUL }; + yield return new object[] { 0xB1800000_003142FFUL, 0x2F4004A5_E17CD24DUL, 0xAF85DC8E_E7E2C5B1UL }; + yield return new object[] { 0x30A1AE18_98FE6B6EUL, 0xB2400000_00034D87UL, 0xB1C3A2F6_E824594EUL }; + yield return new object[] { 0x1040016E_281289C6UL, 0x2E417F41_004E3595UL, 0x0E378B2A_D1798B46UL }; + yield return new object[] { 0x2F2160D1_63A6598CUL, 0xAD988DFF_DCC311D5UL, 0xACC98682_F248B25DUL }; + yield return new object[] { 0xAF800000_000149F7UL, 0x82600001_9308E202UL, 0x0022077B_4806A1EEUL }; + yield return new object[] { 0xB3600000_0000230CUL, 0xB8600000_00000003UL, 0x3A000000_00006924UL }; + yield return new object[] { 0xAD80816C_363173A8UL, 0x2FE00718_1926DD3EUL, 0xAD23F181_FCCBE5B2UL }; + yield return new object[] { 0xB2800000_00000000UL, 0x2F000000_007AF558UL, 0xAFC00000_00000000UL }; + yield return new object[] { 0xAE836513_54C06345UL, 0x8D000000_0000002AUL, 0x09EE421D_FD8E6DBBUL }; + yield return new object[] { 0xAE543A07_C082FDB3UL, 0x35200000_00000007UL, 0xB1CE289F_06C217FDUL }; + yield return new object[] { 0x32600000_0000B48AUL, 0x31400000_00015465UL, 0x31E00000_F00E8272UL }; + yield return new object[] { 0x33400000_000E8F6AUL, 0x33C00000_00000287UL, 0x35400000_24CC74E6UL }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0x31C00000_000001BBUL, 0xAFE00000_41B7F7FCUL }; + yield return new object[] { 0x31200015_EE481751UL, 0xB06385FA_ED15C9E7UL, 0xEC412FEE_DE7C30F6UL }; + yield return new object[] { 0xB3A00000_0001B644UL, 0x31400000_88C9241BUL, 0xB320EA2C_7933C92CUL }; + yield return new object[] { 0xCE000000_2C155ADEUL, 0x2E400000_00000CC1UL, 0xCA800232_3C5BE95EUL }; + yield return new object[] { 0x2E0059A2_1C73F798UL, 0xAEC00000_0000038FUL, 0xAB3FE596_B9AB0231UL }; + yield return new object[] { 0xB4400000_00000000UL, 0xB3C00000_00000002UL, 0x36400000_00000000UL }; + yield return new object[] { 0x14200000_1E148863UL, 0xAF600000_00000003UL, 0x91C00000_5A3D9929UL }; + yield return new object[] { 0xB3000000_093826EBUL, 0x27600000_00000008UL, 0xA8A00000_49C13758UL }; + yield return new object[] { 0xBDA00000_0054E714UL, 0xAEC00000_00083858UL, 0x3AA002B9_E85BCEE0UL }; + yield return new object[] { 0xBBE00000_00000008UL, 0xB1000000_00000008UL, 0x3B200000_00000040UL }; + yield return new object[] { 0xD8600000_72FA8C0BUL, 0x30404C87_C53B7F29UL, 0xD7E5C449_A7AF6E2BUL }; + yield return new object[] { 0x31E00000_00011A74UL, 0x31EB8DA8_BD290BE9UL, 0x32A85A9F_C97EAEF6UL }; + yield return new object[] { 0xAE8000BE_61899933UL, 0x2E8E68EB_22D62BD8UL, 0xACCBC85B_083C3191UL }; + yield return new object[] { 0x30A00002_3C81B477UL, 0xB3400000_0000019EUL, 0xB220039D_D9C1D872UL }; + yield return new object[] { 0xAFC00016_E7160E75UL, 0x32600000_00000013UL, 0xB06001B3_26A312AFUL }; + yield return new object[] { 0x328000C6_0667F8FFUL, 0x437376D5_46AB54AAUL, 0x45B08DF4_524121DDUL }; + yield return new object[] { 0x316008E3_5687A600UL, 0x32C00000_000000BCUL, 0x326686F3_8B9DE800UL }; + yield return new object[] { 0x32600011_10EB5AFDUL, 0xB1200000_70F0F514UL, 0xB264EF2F_0CD8F588UL }; + yield return new object[] { 0x2F600000_0000018DUL, 0x34000000_000001F2UL, 0x31A00000_0003044AUL }; + yield return new object[] { 0x3260573E_35DB7C8EUL, 0x328018AA_7862D5CEUL, 0x34A93E11_AF666CB4UL }; + yield return new object[] { 0x22C00000_00000005UL, 0xB0A00000_000004FDUL, 0xA1A00000_000018F1UL }; + yield return new object[] { 0xD4200014_54458F18UL, 0xB2C00000_0000001DUL, 0x5520024D_8BE135B8UL }; + yield return new object[] { 0x30200000_0000004CUL, 0x324199F5_8C48AAF6UL, 0x30CC2BB0_908EACE7UL }; + yield return new object[] { 0xEB90A2E6_7C00A0EDUL, 0xB2C00000_000020F5UL, 0x2FDB8907_874A27EDUL }; + yield return new object[] { 0xB282B0EE_E060C24AUL, 0x30600000_00000008UL, 0xB1358777_03061250UL }; + yield return new object[] { 0x2E20004A_73ED6947UL, 0x9C6F546E_573DB6B7UL, 0x9A44E6EC_D9584235UL }; + yield return new object[] { 0xB1600000_00000054UL, 0x30800000_00000500UL, 0xB0200000_0001A400UL }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0x31800000_00000055UL, 0xAA162696_B99E0327UL }; + yield return new object[] { 0xB2E00000_00000366UL, 0xB2C00000_00001F1DUL, 0x33E00000_0069BC8EUL }; + yield return new object[] { 0x33000000_00000002UL, 0x33200000_02DF7470UL, 0x34600000_05BEE8E0UL }; + yield return new object[] { 0xB062FAF8_87469CE0UL, 0x30200000_00000045UL, 0xAEF4907F_A5673A70UL }; + yield return new object[] { 0xB0200000_000E661FUL, 0xB1000000_00076DFBUL, 0x2F60006A_FA725365UL }; + yield return new object[] { 0xA9800000_01ECEE93UL, 0x2DC04A74_C2BEB838UL, 0xA6496549_D0B127DBUL }; + yield return new object[] { 0x29200001_1579BC2EUL, 0xAFC058BC_B025DCC1UL, 0xA83022F2_73C51F9FUL }; + yield return new object[] { 0xB3C00000_00000012UL, 0xD5600000_0000001EUL, 0x57600000_0000021CUL }; + yield return new object[] { 0xAF400000_000003E0UL, 0xB2E00000_1892B9E7UL, 0x3060005F_38905F20UL }; + yield return new object[] { 0x32400000_1E910857UL, 0xB120000E_041E212AUL, 0xB22AF7B6_BA92F217UL }; + yield return new object[] { 0x2FE00000_36F3932DUL, 0x32000000_00003529UL, 0x30200B69_3A7AE335UL }; + yield return new object[] { 0x32C00001_B3BF92FFUL, 0x2FC00000_02D2E250UL, 0x310C4DF4_84C9C542UL }; + } + + [Theory] + [MemberData(nameof(op_Multiply_TestData))] + public static void op_Multiply(ulong left, ulong right, ulong expected) + { + Decimal64 result = Unsafe.BitCast(left) * Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Division_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0x31C00000_00000001UL, 0xFC000000_00000000UL }; // NaN / 1 -> NaN + yield return new object[] { 0x31C00000_00000001UL, 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // 1 / NaN -> NaN + yield return new object[] { 0xFC000000_00000000UL, 0x78000000_00000000UL, 0xFC000000_00000000UL }; // NaN / +Inf -> NaN + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL, 0x7C000000_00000000UL }; // +Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000_00000000UL, 0xF8000000_00000000UL, 0x7C000000_00000000UL }; // +Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000_00000000UL, 0x78000000_00000000UL, 0x7C000000_00000000UL }; // -Inf / +Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL, 0x7C000000_00000000UL }; // -Inf / -Inf -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // +Inf / 1 -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0xB1C00000_00000001UL, 0xF8000000_00000000UL }; // +Inf / -1 -> -Inf + yield return new object[] { 0xF8000000_00000000UL, 0x31C00000_00000002UL, 0xF8000000_00000000UL }; // -Inf / 2 -> -Inf + yield return new object[] { 0xF8000000_00000000UL, 0xB1C00000_00000002UL, 0x78000000_00000000UL }; // -Inf / -2 -> +Inf + yield return new object[] { 0x78000000_00000000UL, 0x31C00000_00000000UL, 0x78000000_00000000UL }; // +Inf / +0 -> +Inf + yield return new object[] { 0xF8000000_00000000UL, 0x31C00000_00000000UL, 0xF8000000_00000000UL }; // -Inf / +0 -> -Inf + yield return new object[] { 0x78000000_00000000UL, 0xB1C00000_00000000UL, 0xF8000000_00000000UL }; // +Inf / -0 -> -Inf + yield return new object[] { 0x31C00000_00000001UL, 0x78000000_00000000UL, 0x00000000_00000000UL }; // 1 / +Inf -> +0 (Etiny) + yield return new object[] { 0xB1C00000_00000001UL, 0x78000000_00000000UL, 0x80000000_00000000UL }; // -1 / +Inf -> -0 (Etiny) + yield return new object[] { 0x31C00000_00000005UL, 0xF8000000_00000000UL, 0x80000000_00000000UL }; // 5 / -Inf -> -0 (Etiny) + yield return new object[] { 0x33000000_00000005UL, 0x78000000_00000000UL, 0x00000000_00000000UL }; // 5e10 / +Inf -> +0 (Etiny, dividend exp ignored) + yield return new object[] { 0x31C00000_00000000UL, 0x78000000_00000000UL, 0x00000000_00000000UL }; // +0 / +Inf -> +0 (Etiny) + yield return new object[] { 0xB1C00000_00000000UL, 0x78000000_00000000UL, 0x80000000_00000000UL }; // -0 / +Inf -> -0 (Etiny) + yield return new object[] { 0x31C00000_00000005UL, 0x31C00000_00000000UL, 0x78000000_00000000UL }; // 5 / +0 -> +Inf + yield return new object[] { 0xB1C00000_00000005UL, 0x31C00000_00000000UL, 0xF8000000_00000000UL }; // -5 / +0 -> -Inf + yield return new object[] { 0x31C00000_00000005UL, 0xB1C00000_00000000UL, 0xF8000000_00000000UL }; // 5 / -0 -> -Inf + yield return new object[] { 0xB1C00000_00000005UL, 0xB1C00000_00000000UL, 0x78000000_00000000UL }; // -5 / -0 -> +Inf + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000000UL, 0x7C000000_00000000UL }; // +0 / +0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0xB1C00000_00000000UL, 0xB1C00000_00000000UL, 0x7C000000_00000000UL }; // -0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000000UL, 0x7C000000_00000000UL }; // +0 / -0 -> +QNaN (canonical invalid-operation result) + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000005UL, 0x31C00000_00000000UL }; // +0 / 5 -> +0 (ideal exp) + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000005UL, 0xB1C00000_00000000UL }; // -0 / 5 -> -0 (ideal exp, sign xor) + yield return new object[] { 0x32000000_00000000UL, 0x31C00000_00000005UL, 0x32000000_00000000UL }; // 0e2 / 5 -> 0e2 (ideal exp) + yield return new object[] { 0x31C00000_00000000UL, 0x32200000_00000005UL, 0x31600000_00000000UL }; // 0 / 5e3 -> 0e-3 (ideal exp) + yield return new object[] { 0x32600000_00000000UL, 0x31200000_00000005UL, 0x33000000_00000000UL }; // 0e5 / 5e-5 -> 0e10 (ideal exp) + yield return new object[] { 0x5FE00000_00000000UL, 0x31200000_00000005UL, 0x5FE00000_00000000UL }; // zero dividend, ideal exp far above max quantum (clamped) + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000002UL, 0x31A00000_00000005UL }; // 1 / 2 -> 0.5 (exact) + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000008UL, 0x31600000_0000007DUL }; // 1 / 8 -> 0.125 (exact) + yield return new object[] { 0x31C00000_00000064UL, 0x31C00000_00000004UL, 0x31C00000_00000019UL }; // 100 / 4 -> 25 (exact) + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_00000002UL, 0x31C00000_00000005UL }; // 10 / 2 -> 5 (exact) + yield return new object[] { 0x31C00000_00000006UL, 0x31C00000_00000003UL, 0x31C00000_00000002UL }; // 6 / 3 -> 2 (exact) + yield return new object[] { 0x31C00000_00000005UL, 0x31C00000_00000005UL, 0x31C00000_00000001UL }; // 5 / 5 -> 1 (exact) + yield return new object[] { 0x31800000_00000064UL, 0x31C00000_00000004UL, 0x31800000_00000019UL }; // 1.00 / 4 -> 0.25 (exact, ideal exp preserved) + yield return new object[] { 0x31C00000_00000007UL, 0x31C00000_00000002UL, 0x31A00000_00000023UL }; // 7 / 2 -> 3.5 (exact) + yield return new object[] { 0xB1C00000_0000000FUL, 0x31C00000_00000004UL, 0xB1800000_00000177UL }; // -15 / 4 -> -3.75 (exact, sign) + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000003UL, 0x2FCBD7A6_25405555UL }; // 1 / 3 -> repeating (round) + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000003UL, 0x2FD7AF4C_4A80AAABUL }; // 2 / 3 -> repeating (round) + yield return new object[] { 0x31C00000_000F4240UL, 0x31C00000_00000007UL, 0x30851347_34894925UL }; // 1000000 / 7 (round) + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_00000003UL, 0x2FEBD7A6_25405555UL }; // 10 / 3 -> repeating (round) + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x31C00000_00000007UL, 0x31C51347_34894924UL }; // all-nines / 7 (round) + yield return new object[] { 0x31C00000_00000001UL, 0x6C7386F2_6FC0FFFFUL, 0x2DE38D7E_A4C68000UL }; // 1 / all-nines (round) + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000007UL, 0x2FCA268E_69129249UL }; // 2 / 7 (round) + yield return new object[] { 0x2FE4BCA8_DBB35555UL, 0x2FE650E1_24EF1C71UL, 0x2FDAA535_D3D0C001UL }; // full-precision / full-precision (round) + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x2FE3F28C_B71571C7UL, 0x33A00000_00000009UL }; // all-nines / near-one full precision (round) + yield return new object[] { 0x5FFFF973_CAFA8000UL, 0x31200000_00000001UL, 0x78000000_00000000UL }; // huge / tiny -> +Inf (overflow) + yield return new object[] { 0xDFFFF973_CAFA8000UL, 0x31200000_00000001UL, 0xF8000000_00000000UL }; // -huge / tiny -> -Inf (overflow) + yield return new object[] { 0x01C00000_00000001UL, 0x32600000_00000001UL, 0x01200000_00000001UL }; // tiny / large -> underflow + yield return new object[] { 0x31C00000_00000001UL, 0x5FC00000_00000003UL, 0x01CBD7A6_25405555UL }; // 1 / huge -> tiny subnormal (round) + yield return new object[] { 0x01E00000_00000001UL, 0x32600000_00000001UL, 0x01400000_00000001UL }; // small / large (subnormal-ish) + yield return new object[] { 0x78000000_00000002UL, 0x31C00000_00000001UL, 0x78000000_00000000UL }; // non-canonical +Inf / 1 -> canonical +Inf + yield return new object[] { 0x31C00000_00000001UL, 0xF8000000_00000005UL, 0x80000000_00000000UL }; // 1 / non-canonical -Inf -> canonical -0 (Etiny) + yield return new object[] { 0xAF200000_000023D2UL, 0xAFA34A58_43C82F35UL, 0x6B7B2CEB_297ED958UL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0xB2400000_000014EDUL, 0x979D2147_81117476UL }; + yield return new object[] { 0x31200054_42297730UL, 0x2F000063_A23BC84AUL, 0x31FE0B6E_C9E4B663UL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0x328000E1_F407BFE1UL, 0x2BB3CB6E_24C9CCB5UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0x14600009_ED261C68UL, 0xB0E5373B_9B321342UL }; + yield return new object[] { 0xB1E00000_173C0E97UL, 0x17C00575_228BEDE5UL, 0xC9771439_895B2784UL }; + yield return new object[] { 0x31C00000_3838FC8EUL, 0x33800000_0008C901UL, 0x2E85D20C_CE429AB3UL }; + yield return new object[] { 0x33E00000_00000006UL, 0xB3000000_000738AFUL, 0xB024810D_4C0C6020UL }; + yield return new object[] { 0xB120007E_4A0EE845UL, 0xB1A2CD7D_00E19992UL, 0x2EF86D56_8CCEE558UL }; + yield return new object[] { 0xB1800000_003142FFUL, 0x2F4004A5_E17CD24DUL, 0xB156717A_82887347UL }; + yield return new object[] { 0x30A1AE18_98FE6B6EUL, 0xB2400000_00034D87UL, 0xAF67C2FF_FFD9495DUL }; + yield return new object[] { 0x1040016E_281289C6UL, 0x2E417F41_004E3595UL, 0x118D4239_24D52B06UL }; + yield return new object[] { 0x2F2160D1_63A6598CUL, 0xAD988DFF_DCC311D5UL, 0xB153F0C6_047336B7UL }; + yield return new object[] { 0xAF800000_000149F7UL, 0x82600001_9308E202UL, 0x5C64702C_F70E8C82UL }; + yield return new object[] { 0xB3600000_0000230CUL, 0xB8600000_00000003UL, 0x2B4A9FFE_D84EEAABUL }; + yield return new object[] { 0xAD80816C_363173A8UL, 0x2FE00718_1926DD3EUL, 0xADA67B3F_728271F8UL }; + yield return new object[] { 0xB2800000_00000000UL, 0x2F000000_007AF558UL, 0xB5400000_00000000UL }; + yield return new object[] { 0xAE836513_54C06345UL, 0x8D000000_0000002AUL, 0x5308153A_3780EC5BUL }; + yield return new object[] { 0xAE543A07_C082FDB3UL, 0x35200000_00000007UL, 0xAADCE52F_A54D6A6DUL }; + yield return new object[] { 0x32600000_0000B48AUL, 0x31400000_00015465UL, 0x30F2D7CB_58E04973UL }; + yield return new object[] { 0x33400000_000E8F6AUL, 0x33C00000_00000287UL, 0x2FC53D5A_B1B4EF3CUL }; + yield return new object[] { 0xAFE00000_0025FA34UL, 0x31C00000_000001BBUL, 0xAE73F5C4_5E9939EFUL }; + yield return new object[] { 0x31200015_EE481751UL, 0xB06385FA_ED15C9E7UL, 0xEC01BE15_983D5BE2UL }; + yield return new object[] { 0xB3A00000_0001B644UL, 0x31400000_88C9241BUL, 0xB1B15E7C_6BD1FB18UL }; + yield return new object[] { 0xCE000000_2C155ADEUL, 0x2E400000_00000CC1UL, 0xD0480C36_8CE138FBUL }; + yield return new object[] { 0x2E0059A2_1C73F798UL, 0xAEC00000_0000038FUL, 0xB083D7E6_4738794BUL }; + yield return new object[] { 0xB4400000_00000000UL, 0xB3C00000_00000002UL, 0x32400000_00000000UL }; + yield return new object[] { 0x14200000_1E148863UL, 0xAF600000_00000003UL, 0x96800000_0A06D821UL }; + yield return new object[] { 0xB3000000_093826EBUL, 0x27600000_00000008UL, 0xBD000004_806B00BFUL }; + yield return new object[] { 0xBDA00000_0054E714UL, 0xAEC00000_00083858UL, 0x3EE3AB63_2787AAABUL }; + yield return new object[] { 0xBBE00000_00000008UL, 0xB1000000_00000008UL, 0x3CA00000_00000001UL }; + yield return new object[] { 0xD8600000_72FA8C0BUL, 0x30404C87_C53B7F29UL, 0xD76824FD_5DC6387AUL }; + yield return new object[] { 0x31E00000_00011A74UL, 0x31EB8DA8_BD290BE9UL, 0x2E87E644_72962839UL }; + yield return new object[] { 0xAE8000BE_61899933UL, 0x2E8E68EB_22D62BD8UL, 0xAF672984_2954D3E5UL }; + yield return new object[] { 0x30A00002_3C81B477UL, 0xB3400000_0000019EUL, 0xAE283E16_28D505EEUL }; + yield return new object[] { 0xAFC00016_E7160E75UL, 0x32600000_00000013UL, 0xAE72649C_3A014DDBUL }; + yield return new object[] { 0x328000C6_0667F8FFUL, 0x437376D5_46AB54AAUL, 0x1E8583E6_67068B52UL }; + yield return new object[] { 0x316008E3_5687A600UL, 0x32C00000_000000BCUL, 0x2FD277AE_2C929D9EUL }; + yield return new object[] { 0x32600011_10EB5AFDUL, 0xB1200000_70F0F514UL, 0xB14DBE35_89D2645BUL }; + yield return new object[] { 0x2F600000_0000018DUL, 0x34000000_000001F2UL, 0x2B3C5263_B59E7BE3UL }; + yield return new object[] { 0x3260573E_35DB7C8EUL, 0x328018AA_7862D5CEUL, 0x2FCC90DF_5F1B1DBCUL }; + yield return new object[] { 0x22C00000_00000005UL, 0xB0A00000_000004FDUL, 0xA1ADE90F_5674DD52UL }; + yield return new object[] { 0xD4200014_54458F18UL, 0xB2C00000_0000001DUL, 0x526AB24E_6D558412UL }; + yield return new object[] { 0x30200000_0000004CUL, 0x324199F5_8C48AAF6UL, 0x2C25FD76_8E35F895UL }; + yield return new object[] { 0xEB90A2E6_7C00A0EDUL, 0xB2C00000_000020F5UL, 0x2CE3DE44_E337731CUL }; + yield return new object[] { 0xB282B0EE_E060C24AUL, 0x30600000_00000008UL, 0xECE9A3A9_F4B97C9DUL }; + yield return new object[] { 0x2E20004A_73ED6947UL, 0x9C6F546E_573DB6B7UL, 0xC11A5413_6BDEAB49UL }; + yield return new object[] { 0xB1600000_00000054UL, 0x30800000_00000500UL, 0xB1E00000_00010059UL }; + yield return new object[] { 0xAA4042B6_7AA7818BUL, 0x31800000_00000055UL, 0xAA1EA890_D66400D2UL }; + yield return new object[] { 0xB2E00000_00000366UL, 0xB2C00000_00001F1DUL, 0x2FE3E16B_F510BDF3UL }; + yield return new object[] { 0x33000000_00000002UL, 0x33200000_02DF7470UL, 0x2ECEBDEE_F33FAD55UL }; + yield return new object[] { 0xB062FAF8_87469CE0UL, 0x30200000_00000045UL, 0xB1C451C1_3AC6CD18UL }; + yield return new object[] { 0xB0200000_000E661FUL, 0xB1000000_00076DFBUL, 0x2F06E2A3_F0EE7805UL }; + yield return new object[] { 0xA9800000_01ECEE93UL, 0x2DC04A74_C2BEB838UL, 0xAACE04F2_43140E20UL }; + yield return new object[] { 0x29200001_1579BC2EUL, 0xAFC058BC_B025DCC1UL, 0xA8B0F380_7A2A5151UL }; + yield return new object[] { 0xB3C00000_00000012UL, 0xD5600000_0000001EUL, 0x10000000_00000006UL }; + yield return new object[] { 0xAF400000_000003E0UL, 0xB2E00000_1892B9E7UL, 0x2B888C6C_1C593DF8UL }; + yield return new object[] { 0x32400000_1E910857UL, 0xB120000E_041E212AUL, 0xB0BE43D2_A6ACD64EUL }; + yield return new object[] { 0x2FE00000_36F3932DUL, 0x32000000_00003529UL, 0x2E58114F_9968A5F7UL }; + yield return new object[] { 0x32C00001_B3BF92FFUL, 0x2FC00000_02D2E250UL, 0x33257B7B_B885F394UL }; + } + + [Theory] + [MemberData(nameof(op_Division_TestData))] + public static void op_Division(ulong left, ulong right, ulong expected) + { + Decimal64 result = Unsafe.BitCast(left) / Unsafe.BitCast(right); + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Increment_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // NaN + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL }; // +Inf + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL }; // -Inf + yield return new object[] { 0x78000000_00000002UL, 0x78000000_00000000UL }; // non-canonical +Inf (canonicalizes) + yield return new object[] { 0x31C00000_00000000UL, 0x31C00000_00000001UL }; // +0 -> 1 + yield return new object[] { 0xB1C00000_00000000UL, 0x31C00000_00000001UL }; // -0 -> 1 + yield return new object[] { 0x32600000_00000000UL, 0x31C00000_00000001UL }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { 0x31200000_00000000UL, 0x31200000_000186A0UL }; // 0e-5 + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000002UL }; // 1 -> 2 + yield return new object[] { 0xB1C00000_00000001UL, 0x31C00000_00000000UL }; // -1 -> 0 + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000003UL }; // 2 + yield return new object[] { 0xB1C00000_00000002UL, 0xB1C00000_00000001UL }; // -2 + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_0000000BUL }; // 10 + yield return new object[] { 0x31A00000_00000005UL, 0x31A00000_0000000FUL }; // 0.5 -> 1.5 + yield return new object[] { 0xB1A00000_00000005UL, 0x31A00000_00000005UL }; // -0.5 + yield return new object[] { 0x31A00000_00000019UL, 0x31A00000_00000023UL }; // 2.5 + yield return new object[] { 0x31A00000_00000001UL, 0x31A00000_0000000BUL }; // 0.1 -> 1.1 + yield return new object[] { 0x31800000_00000177UL, 0x31800000_000001DBUL }; // 3.75 + yield return new object[] { 0xB1800000_00000019UL, 0x31800000_0000004BUL }; // -0.25 + yield return new object[] { 0x31C00000_00000009UL, 0x31C00000_0000000AUL }; // 9 -> 10 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x31E38D7E_A4C68000UL }; // all-nines (overflows precision, rounds) + yield return new object[] { 0x31C38D7E_A4C68000UL, 0x31C38D7E_A4C68001UL }; // 10^(P-1) + yield return new object[] { 0x31C38D7E_A4C67FFFUL, 0x31C38D7E_A4C68000UL }; // (P-1)-nines + yield return new object[] { 0x34000000_00000001UL, 0x32238D7E_A4C68000UL }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { 0x5FFFF973_CAFA8000UL, 0x5FFFF973_CAFA8000UL }; // near-max (1 negligible) + yield return new object[] { 0xDFFFF973_CAFA8000UL, 0xDFFFF973_CAFA8000UL }; // near -max (1 negligible) + yield return new object[] { 0x01C00000_00000001UL, 0x2FE38D7E_A4C68000UL }; // tiny positive subnormal (++ ~ 1) + yield return new object[] { 0x81C00000_00000001UL, 0x2FE38D7E_A4C68000UL }; // tiny negative subnormal (++ ~ 1) + yield return new object[] { 0x31200000_00000001UL, 0x31200000_000186A1UL }; // 1e-5 + yield return new object[] { 0x2FE4BCA8_DBB35555UL, 0x2FE84A27_8079D555UL }; // 1.333... full precision + yield return new object[] { 0x6BFB86F2_6FC0FFFFUL, 0x3003E871_B540C000UL }; // 9.999... full precision (carry) + yield return new object[] { 0xDE000D7B_C1739419UL, 0xDDC54457_9125D9C4UL }; + yield return new object[] { 0x33A00000_00000035UL, 0x31F2D452_694F4000UL }; + yield return new object[] { 0x32A00000_001AACBFUL, 0x31C00FE6_3FF64981UL }; + yield return new object[] { 0x31400000_0000C292UL, 0x31400000_0000E9A2UL }; + yield return new object[] { 0xB0E00000_00002125UL, 0x30E00000_0098755BUL }; + yield return new object[] { 0xB4200000_00000004UL, 0xB24E35FA_931A0000UL }; + yield return new object[] { 0x31C00000_06E71450UL, 0x31C00000_06E71451UL }; + yield return new object[] { 0xB2E00000_05A136AAUL, 0xEC798E4D_53BD6A00UL }; + yield return new object[] { 0x2FA00000_03D3544BUL, 0x2FE38D7E_A4D04B15UL }; + yield return new object[] { 0xB0C00000_0002002AUL, 0x30C00000_05F3E0D6UL }; + yield return new object[] { 0xB2604C77_D2CE2737UL, 0xB23DDECE_5887517CUL }; + yield return new object[] { 0x32600000_01C6C941UL, 0x31C002B5_F2D6CEA1UL }; + yield return new object[] { 0x30E00000_33939110UL, 0x30E00000_342C2790UL }; + yield return new object[] { 0x32C00000_5AC09507UL, 0x320568C5_11FA0FC0UL }; + yield return new object[] { 0xB06D3F1C_656F52FAUL, 0xB06D3F05_1CF86AFAUL }; + yield return new object[] { 0x31E00000_00000043UL, 0x31C00000_0000029FUL }; + yield return new object[] { 0xAE00C898_D85C6FF7UL, 0x6BF386F2_6FC0FFFEUL }; + yield return new object[] { 0xBE8CD924_B6560750UL, 0xBE8CD924_B6560750UL }; + yield return new object[] { 0x3102EDC1_D68C47BEUL, 0x3102EDC1_D69B89FEUL }; + yield return new object[] { 0xB0E00000_00000009UL, 0x30E00000_00989677UL }; + yield return new object[] { 0xB0600586_0DF1C1B6UL, 0xB060056E_C57AD9B6UL }; + yield return new object[] { 0x33000000_00000016UL, 0x31C00033_39059801UL }; + yield return new object[] { 0xB0C0007E_EB0B6EA0UL, 0xB0C0007E_E5158DA0UL }; + yield return new object[] { 0x2F400000_00000897UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0x32A00000_000003E0UL, 0x31C00002_4F473001UL }; + yield return new object[] { 0xB2A00000_00000004UL, 0xB1C00000_026259FFUL }; + yield return new object[] { 0x3820253B_3AF58D7CUL, 0x37EE8B23_07EB4470UL }; + yield return new object[] { 0x32A00000_0060035AUL, 0x31C0393A_6F686901UL }; + yield return new object[] { 0x2EC00000_1B1D8BA6UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0xB2800000_22850479UL, 0xB1C20EBA_2F7F503FUL }; + yield return new object[] { 0xB2200000_30F3774BUL, 0xB1C000BF_3709FCF7UL }; + yield return new object[] { 0xAF200000_042E8A44UL, 0x6BF386F2_6FC0FD42UL }; + yield return new object[] { 0x302012E4_B193AA00UL, 0x30201BFD_00064A00UL }; + yield return new object[] { 0xAEE00001_BE122682UL, 0x6BF386F2_6FC0FD14UL }; + yield return new object[] { 0x07600000_00000000UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0xB2600000_0000D120UL, 0xB1C00001_3F1973FFUL }; + yield return new object[] { 0x2EC00008_5153EF36UL, 0x2FE38D7E_A4C68024UL }; + yield return new object[] { 0x2F200000_00000045UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0x2FCDCB8B_5E0C05CAUL, 0x2FE4EEA6_2E2E1A2EUL }; + yield return new object[] { 0xBEE000E4_BBA70FA7UL, 0xEF9AE6E2_2DD36B70UL }; + yield return new object[] { 0x5B600000_000003D2UL, 0x7672BEDB_B1E74000UL }; + yield return new object[] { 0x2F600000_00004436UL, 0x2FE38D7E_A4C68002UL }; + yield return new object[] { 0x30600000_00833DA9UL, 0x30600017_48FA25A9UL }; + yield return new object[] { 0x2F200000_1228ADE2UL, 0x2FE38D7E_A4C68131UL }; + yield return new object[] { 0xAF600000_2AD1A374UL, 0x6BF386F2_6FB609D2UL }; + yield return new object[] { 0xB1600000_18006133UL, 0xB1600000_18005D4BUL }; + yield return new object[] { 0x6B5A3F19_333501CCUL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0x32E00000_001FC878UL, 0x31C7666B_545EB001UL }; + yield return new object[] { 0x31800000_B171BD11UL, 0x31800000_B171BD75UL }; + yield return new object[] { 0x2EE00001_723C5118UL, 0x2FE38D7E_A4C6803EUL }; + yield return new object[] { 0x9F400000_00001DF5UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0xAEC00000_001D0824UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0xAFA34A58_43C82F35UL, 0x6BF332B6_68F9C814UL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0x2FE38D7E_A4C68000UL }; + yield return new object[] { 0xB2400000_000014EDUL, 0xB1C00000_033169CFUL }; + yield return new object[] { 0x31200054_42297730UL, 0x31200054_422AFDD0UL }; + yield return new object[] { 0x2F000063_A23BC84AUL, 0x2FE38D7E_A4C72728UL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0x2FE38D7E_A4C68001UL }; + yield return new object[] { 0x328000E1_F407BFE1UL, 0x6C827A4C_6EB74510UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0x2FE38D7E_A4C68000UL }; + } + + [Theory] + [MemberData(nameof(op_Increment_TestData))] + public static void op_Increment(ulong value, ulong expected) + { + Decimal64 result = Unsafe.BitCast(value); + result++; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + public static IEnumerable op_Decrement_TestData() + { + yield return new object[] { 0xFC000000_00000000UL, 0xFC000000_00000000UL }; // NaN + yield return new object[] { 0x78000000_00000000UL, 0x78000000_00000000UL }; // +Inf + yield return new object[] { 0xF8000000_00000000UL, 0xF8000000_00000000UL }; // -Inf + yield return new object[] { 0x78000000_00000002UL, 0x78000000_00000000UL }; // non-canonical +Inf (canonicalizes) + yield return new object[] { 0x31C00000_00000000UL, 0xB1C00000_00000001UL }; // +0 -> -1 + yield return new object[] { 0xB1C00000_00000000UL, 0xB1C00000_00000001UL }; // -0 -> -1 + yield return new object[] { 0x32600000_00000000UL, 0xB1C00000_00000001UL }; // 0e5 (preferred exponent min(exp,0)) + yield return new object[] { 0x31200000_00000000UL, 0xB1200000_000186A0UL }; // 0e-5 + yield return new object[] { 0x31C00000_00000001UL, 0x31C00000_00000000UL }; // 1 -> 0 + yield return new object[] { 0xB1C00000_00000001UL, 0xB1C00000_00000002UL }; // -1 -> -2 + yield return new object[] { 0x31C00000_00000002UL, 0x31C00000_00000001UL }; // 2 + yield return new object[] { 0xB1C00000_00000002UL, 0xB1C00000_00000003UL }; // -2 + yield return new object[] { 0x31C00000_0000000AUL, 0x31C00000_00000009UL }; // 10 + yield return new object[] { 0x31A00000_00000005UL, 0xB1A00000_00000005UL }; // 0.5 -> -0.5 + yield return new object[] { 0xB1A00000_00000005UL, 0xB1A00000_0000000FUL }; // -0.5 + yield return new object[] { 0x31A00000_00000019UL, 0x31A00000_0000000FUL }; // 2.5 + yield return new object[] { 0x31A00000_00000001UL, 0xB1A00000_00000009UL }; // 0.1 -> -0.9 + yield return new object[] { 0x31800000_00000177UL, 0x31800000_00000113UL }; // 3.75 + yield return new object[] { 0xB1800000_00000019UL, 0xB1800000_0000007DUL }; // -0.25 + yield return new object[] { 0x31C00000_00000009UL, 0x31C00000_00000008UL }; // 9 -> 8 + yield return new object[] { 0x6C7386F2_6FC0FFFFUL, 0x6C7386F2_6FC0FFFEUL }; // all-nines (decrement in last place) + yield return new object[] { 0x31C38D7E_A4C68000UL, 0x31C38D7E_A4C67FFFUL }; // 10^(P-1) + yield return new object[] { 0x31C38D7E_A4C67FFFUL, 0x31C38D7E_A4C67FFEUL }; // (P-1)-nines + yield return new object[] { 0x34000000_00000001UL, 0x32238D7E_A4C68000UL }; // 1e(P+2) (1 below quantum, no visible change) + yield return new object[] { 0x5FFFF973_CAFA8000UL, 0x5FFFF973_CAFA8000UL }; // near-max (1 negligible) + yield return new object[] { 0xDFFFF973_CAFA8000UL, 0xDFFFF973_CAFA8000UL }; // near -max (1 negligible) + yield return new object[] { 0x01C00000_00000001UL, 0xAFE38D7E_A4C68000UL }; // tiny positive subnormal (-- ~ -1) + yield return new object[] { 0x81C00000_00000001UL, 0xAFE38D7E_A4C68000UL }; // tiny negative subnormal (-- ~ -1) + yield return new object[] { 0x31200000_00000001UL, 0xB1200000_0001869FUL }; // 1e-5 + yield return new object[] { 0x2FE4BCA8_DBB35555UL, 0x2FE12F2A_36ECD555UL }; // 1.333... full precision + yield return new object[] { 0x6BFB86F2_6FC0FFFFUL, 0x2FFFF973_CAFA7FFFUL }; // 9.999... full precision + yield return new object[] { 0xDE000D7B_C1739419UL, 0xDDC54457_9125D9C4UL }; + yield return new object[] { 0x33A00000_00000035UL, 0x31F2D452_694F4000UL }; + yield return new object[] { 0x32A00000_001AACBFUL, 0x31C00FE6_3FF6497FUL }; + yield return new object[] { 0x31400000_0000C292UL, 0x31400000_00009B82UL }; + yield return new object[] { 0xB0E00000_00002125UL, 0xB0E00000_0098B7A5UL }; + yield return new object[] { 0xB4200000_00000004UL, 0xB24E35FA_931A0000UL }; + yield return new object[] { 0x31C00000_06E71450UL, 0x31C00000_06E7144FUL }; + yield return new object[] { 0xB2E00000_05A136AAUL, 0xEC798E4D_53BD6A00UL }; + yield return new object[] { 0x2FA00000_03D3544BUL, 0xEBF386F2_6F5F112CUL }; + yield return new object[] { 0xB0C00000_0002002AUL, 0xB0C00000_05F7E12AUL }; + yield return new object[] { 0xB2604C77_D2CE2737UL, 0xB23DDECE_5887517CUL }; + yield return new object[] { 0x32600000_01C6C941UL, 0x31C002B5_F2D6CE9FUL }; + yield return new object[] { 0x30E00000_33939110UL, 0x30E00000_32FAFA90UL }; + yield return new object[] { 0x32C00000_5AC09507UL, 0x320568C5_11FA0FC0UL }; + yield return new object[] { 0xB06D3F1C_656F52FAUL, 0xB06D3F33_ADE63AFAUL }; + yield return new object[] { 0x31E00000_00000043UL, 0x31C00000_0000029DUL }; + yield return new object[] { 0xAE00C898_D85C6FF7UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0xBE8CD924_B6560750UL, 0xBE8CD924_B6560750UL }; + yield return new object[] { 0x3102EDC1_D68C47BEUL, 0x3102EDC1_D67D057EUL }; + yield return new object[] { 0xB0E00000_00000009UL, 0xB0E00000_00989689UL }; + yield return new object[] { 0xB0600586_0DF1C1B6UL, 0xB060059D_5668A9B6UL }; + yield return new object[] { 0x33000000_00000016UL, 0x31C00033_390597FFUL }; + yield return new object[] { 0xB0C0007E_EB0B6EA0UL, 0xB0C0007E_F1014FA0UL }; + yield return new object[] { 0x2F400000_00000897UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0x32A00000_000003E0UL, 0x31C00002_4F472FFFUL }; + yield return new object[] { 0xB2A00000_00000004UL, 0xB1C00000_02625A01UL }; + yield return new object[] { 0x3820253B_3AF58D7CUL, 0x37EE8B23_07EB4470UL }; + yield return new object[] { 0x32A00000_0060035AUL, 0x31C0393A_6F6868FFUL }; + yield return new object[] { 0x2EC00000_1B1D8BA6UL, 0xEBF386F2_6FC0FFFBUL }; + yield return new object[] { 0xB2800000_22850479UL, 0xB1C20EBA_2F7F5041UL }; + yield return new object[] { 0xB2200000_30F3774BUL, 0xB1C000BF_3709FCF9UL }; + yield return new object[] { 0xAF200000_042E8A44UL, 0xAFE38D7E_A4C68046UL }; + yield return new object[] { 0x302012E4_B193AA00UL, 0x302009CC_63210A00UL }; + yield return new object[] { 0xAEE00001_BE122682UL, 0xAFE38D7E_A4C6804BUL }; + yield return new object[] { 0x07600000_00000000UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0xB2600000_0000D120UL, 0xB1C00001_3F197401UL }; + yield return new object[] { 0x2EC00008_5153EF36UL, 0xEBF386F2_6FC0FE9BUL }; + yield return new object[] { 0x2F200000_00000045UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0x2FCDCB8B_5E0C05CAUL, 0xAFD5BB67_11B4FA36UL }; + yield return new object[] { 0xBEE000E4_BBA70FA7UL, 0xEF9AE6E2_2DD36B70UL }; + yield return new object[] { 0x5B600000_000003D2UL, 0x7672BEDB_B1E74000UL }; + yield return new object[] { 0x2F600000_00004436UL, 0xEBF386F2_6FC0FFEFUL }; + yield return new object[] { 0x30600000_00833DA9UL, 0xB0600017_47F3AA57UL }; + yield return new object[] { 0x2F200000_1228ADE2UL, 0xEBF386F2_6FC0F419UL }; + yield return new object[] { 0xAF600000_2AD1A374UL, 0xAFE38D7E_A4C7989EUL }; + yield return new object[] { 0xB1600000_18006133UL, 0xB1600000_1800651BUL }; + yield return new object[] { 0x6B5A3F19_333501CCUL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0x32E00000_001FC878UL, 0x31C7666B_545EAFFFUL }; + yield return new object[] { 0x31800000_B171BD11UL, 0x31800000_B171BCADUL }; + yield return new object[] { 0x2EE00001_723C5118UL, 0xEBF386F2_6FC0FD93UL }; + yield return new object[] { 0x9F400000_00001DF5UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0xAEC00000_001D0824UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0xAFA34A58_43C82F35UL, 0xAFE395EB_0BDA6BFEUL }; + yield return new object[] { 0x19C00000_0006B3C8UL, 0xAFE38D7E_A4C68000UL }; + yield return new object[] { 0xB2400000_000014EDUL, 0xB1C00000_033169D1UL }; + yield return new object[] { 0x31200054_42297730UL, 0x31200054_4227F090UL }; + yield return new object[] { 0x2F000063_A23BC84AUL, 0xEBF386F2_6FBA786CUL }; + yield return new object[] { 0x2E60007D_E4F31691UL, 0xEBF386F2_6FC0FFFBUL }; + yield return new object[] { 0x328000E1_F407BFE1UL, 0x6C827A4C_6EB74510UL }; + yield return new object[] { 0x95400091_BAB57FB9UL, 0xAFE38D7E_A4C68000UL }; + } + + [Theory] + [MemberData(nameof(op_Decrement_TestData))] + public static void op_Decrement(ulong value, ulong expected) + { + Decimal64 result = Unsafe.BitCast(value); + result--; + Assert.Equal(expected, Unsafe.BitCast(result)); + } + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() { NumberStyles style = NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters; @@ -575,5 +1534,45 @@ public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, Nu Assert.False(Decimal64.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out int bytesConsumed)); Assert.Equal(0, bytesConsumed); } + + [ConditionalTheory(typeof(DecimalIeee754IntelTestData), nameof(DecimalIeee754IntelTestData.IsAvailable))] + [MemberData(nameof(DecimalIeee754IntelTestData.Decimal64Arithmetic), MemberType = typeof(DecimalIeee754IntelTestData))] + public static void op_Arithmetic_IntelReferenceVectors(string operation, ulong left, ulong right, ulong expected) + { + Decimal64 l = Unsafe.BitCast(left); + Decimal64 r = Unsafe.BitCast(right); + + Decimal64 result = operation switch + { + "add" => l + r, + "sub" => l - r, + "mul" => l * r, + "div" => l / r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, Unsafe.BitCast(result)); + } + + [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) + { + Decimal64 l = Unsafe.BitCast(left); + Decimal64 r = Unsafe.BitCast(right); + + bool result = operation switch + { + "quiet_equal" => l == r, + "quiet_not_equal" => l != r, + "quiet_less" => l < r, + "quiet_greater" => l > r, + "quiet_less_equal" => l <= r, + "quiet_greater_equal" => l >= r, + _ => throw new InvalidOperationException($"Unexpected operation '{operation}'."), + }; + + Assert.Equal(expected, result); + } } } 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 new file mode 100644 index 00000000000000..ef9994795e9bdb --- /dev/null +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalIeee754IntelTestData.cs @@ -0,0 +1,221 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace System.Tests +{ + /// + /// Streams operator test vectors out of the readtest.in file that ships with the Intel(R) + /// Decimal Floating-Point Math Library so the ported , + /// , and operators can be + /// cross-validated bit-for-bit against the reference implementation. + /// + /// + /// The file is very large (tens of thousands of vectors spanning hundreds of operations) and is not + /// redistributed with the runtime, so the theories that consume this data are gated on the + /// condition and skip unless an interested developer copies readtest.in + /// next to the test assembly (that is, into ). This keeps the + /// exhaustive validation available for local or outer-loop runs without redistributing the file or + /// paying its cost on every test pass. Gating on a file the developer deliberately drops into the + /// output directory (rather than an environment variable naming an arbitrary path) avoids reading a + /// file from a location an unrelated process could influence and is no worse than shipping it. + /// + /// Each line is whitespace separated as op rnd a b result flags. Only round-to-nearest-even + /// (rnd == 0) rows whose operands are bracketed hex bit patterns are consumed; rows using + /// decimal-string or named operands (for example QNaN or Infinity), other rounding + /// modes, and the trailing IEEE exception-flags column are ignored. Bid128 operands appear both as + /// [hi,lo] (arithmetic) and [32hex] (comparison); both encodings are handled. + /// + public static class DecimalIeee754IntelTestData + { + private const string ReadTestFileName = "readtest.in"; + + private static readonly string? s_readTestPath = ResolveReadTestPath(); + + private static readonly HashSet s_bid32Arithmetic = new() { "bid32_add", "bid32_sub", "bid32_mul", "bid32_div" }; + private static readonly HashSet s_bid64Arithmetic = new() { "bid64_add", "bid64_sub", "bid64_mul", "bid64_div" }; + private static readonly HashSet s_bid128Arithmetic = new() { "bid128_add", "bid128_sub", "bid128_mul", "bid128_div" }; + + private static readonly HashSet s_bid32Comparison = new() { "bid32_quiet_equal", "bid32_quiet_not_equal", "bid32_quiet_less", "bid32_quiet_greater", "bid32_quiet_less_equal", "bid32_quiet_greater_equal" }; + private static readonly HashSet s_bid64Comparison = new() { "bid64_quiet_equal", "bid64_quiet_not_equal", "bid64_quiet_less", "bid64_quiet_greater", "bid64_quiet_less_equal", "bid64_quiet_greater_equal" }; + private static readonly HashSet s_bid128Comparison = new() { "bid128_quiet_equal", "bid128_quiet_not_equal", "bid128_quiet_less", "bid128_quiet_greater", "bid128_quiet_less_equal", "bid128_quiet_greater_equal" }; + + /// + /// Gets a value indicating whether the Intel readtest.in reference vectors are available, + /// gating the theories that consume them. + /// + public static bool IsAvailable => s_readTestPath is not null; + + public static IEnumerable Decimal32Arithmetic() + { + foreach (string[] fields in EnumerateRows(s_bid32Arithmetic)) + { + if (TryParseBid32(fields[2], out uint left) && TryParseBid32(fields[3], out uint right) && TryParseBid32(fields[4], out uint expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + public static IEnumerable Decimal64Arithmetic() + { + foreach (string[] fields in EnumerateRows(s_bid64Arithmetic)) + { + if (TryParseBid64(fields[2], out ulong left) && TryParseBid64(fields[3], out ulong right) && TryParseBid64(fields[4], out ulong expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + public static IEnumerable Decimal128Arithmetic() + { + foreach (string[] fields in EnumerateRows(s_bid128Arithmetic)) + { + if (TryParseBid128(fields[2], out UInt128 left) && TryParseBid128(fields[3], out UInt128 right) && TryParseBid128(fields[4], out UInt128 expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + public static IEnumerable Decimal32Comparison() + { + foreach (string[] fields in EnumerateRows(s_bid32Comparison)) + { + if (TryParseBid32(fields[2], out uint left) && TryParseBid32(fields[3], out uint right) && TryParseComparisonResult(fields[4], out bool expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + public static IEnumerable Decimal64Comparison() + { + foreach (string[] fields in EnumerateRows(s_bid64Comparison)) + { + if (TryParseBid64(fields[2], out ulong left) && TryParseBid64(fields[3], out ulong right) && TryParseComparisonResult(fields[4], out bool expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + public static IEnumerable Decimal128Comparison() + { + foreach (string[] fields in EnumerateRows(s_bid128Comparison)) + { + if (TryParseBid128(fields[2], out UInt128 left) && TryParseBid128(fields[3], out UInt128 right) && TryParseComparisonResult(fields[4], out bool expected)) + { + yield return new object[] { OperationSuffix(fields[0]), left, right, expected }; + } + } + } + + private static IEnumerable EnumerateRows(HashSet operations) + { + string? path = s_readTestPath; + + if (path is null) + { + yield break; + } + + foreach (string line in File.ReadLines(path)) + { + string[] fields = line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); + + // A usable row is `op 0 a b result flags`; anything shorter, in a different rounding mode, + // or naming an operation we do not care about is skipped. + if ((fields.Length >= 5) && (fields[1] == "0") && operations.Contains(fields[0])) + { + yield return fields; + } + } + } + + private static string OperationSuffix(string operation) => operation.Substring(operation.IndexOf('_') + 1); + + private static bool TryParseComparisonResult(string token, out bool value) + { + switch (token) + { + case "0": + value = false; + return true; + + case "1": + value = true; + return true; + + default: + value = false; + return false; + } + } + + private static bool TryParseBid32(string token, out uint value) + { + value = 0; + + if ((token.Length < 2) || (token[0] != '[') || (token[^1] != ']')) + { + return false; + } + + // Operands are not always zero-padded to the full width, so parse the inner hex directly and + // let uint.TryParse reject anything wider than the type. + return uint.TryParse(token.AsSpan(1, token.Length - 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value); + } + + private static bool TryParseBid64(string token, out ulong value) + { + value = 0; + + if ((token.Length < 2) || (token[0] != '[') || (token[^1] != ']')) + { + return false; + } + + return ulong.TryParse(token.AsSpan(1, token.Length - 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value); + } + + private static bool TryParseBid128(string token, out UInt128 value) + { + value = default; + + if ((token.Length < 2) || (token[0] != '[') || (token[^1] != ']')) + { + return false; + } + + ReadOnlySpan inner = token.AsSpan(1, token.Length - 2); + int comma = inner.IndexOf(','); + + if (comma >= 0) + { + // Bid128 arithmetic operands are encoded as `[hi,lo]`. + if (!ulong.TryParse(inner.Slice(0, comma), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out ulong upper) || + !ulong.TryParse(inner.Slice(comma + 1), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out ulong lower)) + { + return false; + } + + value = new UInt128(upper, lower); + return true; + } + + // Bid128 comparison operands and every bid128 result are encoded as a single `[hex]`. + return UInt128.TryParse(inner, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value); + } + + private static string? ResolveReadTestPath() + { + string path = Path.Combine(AppContext.BaseDirectory, ReadTestFileName); + return File.Exists(path) ? path : null; + } + } +}