Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions src/libraries/System.Private.CoreLib/src/System/Half.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;

namespace System
{
Expand Down Expand Up @@ -157,6 +158,12 @@ internal static ushort ExtractTrailingSignificandFromBits(ushort bits)
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)" />
public static bool operator <(Half left, Half right)
{
if (Avx2.IsSupported)
{
// (float)Half lowers to a hardware conversion here, so comparing as float is cheaper.
return (float)left < (float)right;
}

if (IsNaN(left) || IsNaN(right))
{
// IEEE defines that NaN is unordered with respect to everything, including itself.
Expand Down Expand Up @@ -185,6 +192,12 @@ internal static ushort ExtractTrailingSignificandFromBits(ushort bits)
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)" />
public static bool operator <=(Half left, Half right)
{
if (Avx2.IsSupported)
{
// (float)Half lowers to a hardware conversion here, so comparing as float is cheaper.
return (float)left <= (float)right;
}

if (IsNaN(left) || IsNaN(right))
{
// IEEE defines that NaN is unordered with respect to everything, including itself.
Expand Down Expand Up @@ -437,28 +450,31 @@ public int CompareTo(object? obj)
/// <returns>A value less than zero if this is less than <paramref name="other"/>, zero if this is equal to <paramref name="other"/>, or a value greater than zero if this is greater than <paramref name="other"/>.</returns>
public int CompareTo(Half other)
{
if (this < other)
if (Avx2.IsSupported)
{
return -1;
// (float)Half lowers to a hardware conversion here, so comparing as float is cheaper.
return ((float)this).CompareTo((float)other);
}

if (this > other)
if (IsNaN(this))
{
return 1;
return IsNaN(other) ? 0 : -1;
}

if (this == other)
if (IsNaN(other))
{
return 0;
return 1;
}

if (IsNaN(this))
{
return IsNaN(other) ? 0 : -1;
}
// Neither value is NaN, so map the sign-magnitude bits to a monotonic ordering.
return GetCompareKey(_value) - GetCompareKey(other._value);
}

Debug.Assert(IsNaN(other));
return 1;
private static int GetCompareKey(ushort bits)
{
// Positive maps to 0x8000 + bits and negative maps to 0x8000 - magnitude, so both zeros
// collapse to 0x8000 while the ordering stays monotonic across the finite and infinite range.
return ((bits & SignMask) == 0) ? (SignMask + bits) : (SignMask - (bits & ~SignMask));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,12 @@ public static IEnumerable<object[]> CompareTo_TestData()
yield return new object[] { (Half)(-180f), (Half)(180f), -1};
yield return new object[] { (Half)(180f), (Half)(-180f), 1};
yield return new object[] { (Half)(-65535), (object)null, 1};
yield return new object[] { BitConverter.UInt16BitsToHalf(0x0000), BitConverter.UInt16BitsToHalf(0x8000), 0 }; // +0 vs -0
yield return new object[] { BitConverter.UInt16BitsToHalf(0x8000), BitConverter.UInt16BitsToHalf(0x0000), 0 }; // -0 vs +0
yield return new object[] { BitConverter.UInt16BitsToHalf(0x8001), Half.Epsilon, -1 }; // -subnormal vs +subnormal
yield return new object[] { Half.NaN, Half.PositiveInfinity, -1 };
yield return new object[] { Half.PositiveInfinity, Half.NaN, 1 };
yield return new object[] { Half.NaN, Half.NegativeInfinity, -1 };
}

[Theory]
Expand Down
Loading