diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index d527a4cf583216..7e8c3aefe04104 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -530,6 +530,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs
new file mode 100644
index 00000000000000..d461168433634a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/TotalOrderIeee754Comparer.cs
@@ -0,0 +1,233 @@
+// 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.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace System.Numerics
+{
+ ///
+ /// Represents a comparison operation that compares floating-point numbers
+ /// with IEEE 754 totalOrder semantic.
+ ///
+ /// The type of the numbers to be compared, must be an IEEE 754 floating-point type.
+ public readonly struct TotalOrderIeee754Comparer : IComparer, IEqualityComparer, IEquatable>
+ where T : IFloatingPointIeee754?
+ {
+ ///
+ /// Compares two numbers with IEEE 754 totalOrder semantic and returns
+ /// a value indicating whether one is less than, equal to, or greater than the other.
+ ///
+ /// The first number to compare.
+ /// The second number to compare.
+ ///
+ /// A signed integer that indicates the relative
+ /// values of and , as shown in the following table.
+ ///
+ ///
+ /// Value
+ /// Meaning
+ ///
+ ///
+ /// Less than zero
+ /// is less than
+ ///
+ ///
+ /// Zero
+ /// equals
+ ///
+ ///
+ /// Greater than zero
+ /// is greater than
+ ///
+ ///
+ ///
+ ///
+ /// IEEE 754 specification defines totalOrder as <= semantic.
+ /// totalOrder(x,y) is when the result of this method is less than or equal to 0.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Compare(T? x, T? y)
+ {
+ if (typeof(T) == typeof(float))
+ {
+ return CompareIntegerSemantic(BitConverter.SingleToInt32Bits((float)(object)x!), BitConverter.SingleToInt32Bits((float)(object)y!));
+ }
+ else if (typeof(T) == typeof(double))
+ {
+ return CompareIntegerSemantic(BitConverter.DoubleToInt64Bits((double)(object)x!), BitConverter.DoubleToInt64Bits((double)(object)y!));
+ }
+ else if (typeof(T) == typeof(Half))
+ {
+ return CompareIntegerSemantic(BitConverter.HalfToInt16Bits((Half)(object)x!), BitConverter.HalfToInt16Bits((Half)(object)y!));
+ }
+ else
+ {
+ return CompareGeneric(x, y);
+ }
+
+ static int CompareIntegerSemantic(TInteger x, TInteger y)
+ where TInteger : struct, IBinaryInteger, ISignedNumber
+ {
+ // In IEEE 754 binary floating-point representation, a number is represented as Sign|Exponent|Significand
+ // Normal numbers has an implicit 1. in front of the significand, so value with larger exponent will have larger absolute value
+ // Inf and NaN are defined as Exponent=All 1s, while Inf has Significand=0, sNaN has Significand=0xxx and qNaN has Significand=1xxx
+ // This also satisfies totalOrder definition which is +x < +Inf < +sNaN < +qNaN
+
+ // The order of NaNs of same category and same sign is implementation defined,
+ // here we define it as the order of exponent bits to simplify comparison
+
+ // Negative values are represented in sign-magnitude, instead of two's complement like integers
+ // Just negating the comparison result when both numbers are negative is enough
+
+ return (TInteger.IsNegative(x) && TInteger.IsNegative(y)) ? y.CompareTo(x) : x.CompareTo(y);
+ }
+
+ static int CompareGeneric(T? x, T? y)
+ {
+ // IComparer contract is null < value
+
+ if (x is null)
+ {
+ return (y is null) ? 0 : -1;
+ }
+ else if (y is null)
+ {
+ return 1;
+ }
+
+ // If < or > returns true, the result satisfies definition of totalOrder too
+
+ if (x < y)
+ {
+ return -1;
+ }
+ else if (x > y)
+ {
+ return 1;
+ }
+ else if (x == y)
+ {
+ if (T.IsZero(x)) // only zeros are equal to zeros
+ {
+ // IEEE 754 numbers are either positive or negative. Skip check for the opposite.
+
+ if (T.IsNegative(x))
+ {
+ return T.IsNegative(y) ? 0 : -1;
+ }
+ else
+ {
+ return T.IsPositive(y) ? 0 : 1;
+ }
+ }
+ else
+ {
+ // Equivalant values are compared by their exponent parts,
+ // and the value with smaller exponent is considered closer to zero.
+
+ // This only applies to IEEE 754 decimals. Consider to add support if decimals are added into .NET.
+ return 0;
+ }
+ }
+ else
+ {
+ // One or two of the values are NaN
+ // totalOrder defines that -qNaN < -sNaN < x < +sNaN < + qNaN
+
+ static int CompareSignificand(T x, T y)
+ {
+ // IEEE 754 totalOrder only defines the order of NaN type bit (the first bit of significand)
+ // To match the integer semantic comparison above, here we compare all the significand bits
+ // Revisit this if decimals are added
+
+ // Leave the space for custom floating-point type that has variable significand length
+
+ int xSignificandBits = x!.GetSignificandBitLength();
+ int ySignificandBits = y!.GetSignificandBitLength();
+
+ if (xSignificandBits == ySignificandBits)
+ {
+ // Prevent stack overflow for huge numbers
+ const int StackAllocThreshold = 256;
+
+ int xSignificandLength = x.GetSignificandByteCount();
+ int ySignificandLength = y.GetSignificandByteCount();
+
+ Span significandX = xSignificandLength <= StackAllocThreshold ? stackalloc byte[xSignificandLength] : new byte[xSignificandLength];
+ Span significandY = ySignificandLength <= StackAllocThreshold ? stackalloc byte[ySignificandLength] : new byte[ySignificandLength];
+
+ x.WriteSignificandBigEndian(significandX);
+ y.WriteSignificandBigEndian(significandY);
+
+ return significandX.SequenceCompareTo(significandY);
+ }
+ else
+ {
+ return xSignificandBits.CompareTo(ySignificandBits);
+ }
+ }
+
+ if (T.IsNaN(x))
+ {
+ if (T.IsNaN(y))
+ {
+ if (T.IsNegative(x))
+ {
+ return T.IsPositive(y) ? -1 : CompareSignificand(y, x);
+ }
+ else
+ {
+ return T.IsNegative(y) ? 1 : CompareSignificand(x, y);
+ }
+ }
+ else
+ {
+ return T.IsPositive(x) ? 1 : -1;
+ }
+ }
+ else if (T.IsNaN(y))
+ {
+ return T.IsPositive(y) ? -1 : 1;
+ }
+ else
+ {
+ // T does not correctly implement IEEE754 semantics
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison);
+ return 0; // unreachable
+ }
+ }
+ }
+ }
+
+ ///
+ /// Determines whether the specified numbers are equal.
+ ///
+ /// The first number of type to compare.
+ /// The second number of type to compare.
+ /// if the specified numbers are equal; otherwise, .
+ ///
+ /// There is no corresponding equals semantic with totalOrder defined by IEEE 754 specification.
+ /// This method returns when returns 0.
+ ///
+ public bool Equals(T? x, T? y) => Compare(x, y) == 0;
+
+ ///
+ /// Returns a hash code for the specified number.
+ ///
+ /// The number for which a hash code is to be returned.
+ /// A hash code for the specified number.
+ public int GetHashCode([DisallowNull] T obj)
+ {
+ ArgumentNullException.ThrowIfNull(obj, nameof(obj));
+ return obj.GetHashCode();
+ }
+
+ public bool Equals(TotalOrderIeee754Comparer other) => true;
+
+ public override bool Equals([NotNullWhen(true)] object? obj) => obj is TotalOrderIeee754Comparer;
+
+ public override int GetHashCode() => EqualityComparer.Default.GetHashCode();
+ }
+}
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 6ad2debb088229..d52f990054fcb2 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -10530,6 +10530,15 @@ public partial interface IUnaryPlusOperators where TSelf : Syste
public partial interface IUnsignedNumber : System.Numerics.INumberBase where TSelf : System.Numerics.IUnsignedNumber?
{
}
+ public readonly partial struct TotalOrderIeee754Comparer : System.Collections.Generic.IComparer, System.Collections.Generic.IEqualityComparer, System.IEquatable> where T : System.Numerics.IFloatingPointIeee754?
+ {
+ public int Compare(T? x, T? y) { throw null; }
+ public bool Equals(System.Numerics.TotalOrderIeee754Comparer other) { throw null; }
+ public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; }
+ public bool Equals(T? x, T? y) { throw null; }
+ public override int GetHashCode() { throw null; }
+ public int GetHashCode([System.Diagnostics.CodeAnalysis.DisallowNullAttribute] T obj) { throw null; }
+ }
}
namespace System.Reflection
{
diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
index fc6859854dc2e6..91d560c81a8d8c 100644
--- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
+++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
@@ -113,6 +113,7 @@
+
diff --git a/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs b/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs
new file mode 100644
index 00000000000000..454a60dff8e641
--- /dev/null
+++ b/src/libraries/System.Runtime/tests/System/Numerics/TotalOrderIeee754ComparerTests.cs
@@ -0,0 +1,104 @@
+// 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.Numerics;
+using System.Runtime.InteropServices;
+using Xunit;
+
+namespace System.Runtime.Tests
+{
+ public sealed class TotalOrderIeee754ComparerTests
+ {
+ public static IEnumerable