diff --git a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs index f45b88c9478d4..32dd05facb031 100644 --- a/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs +++ b/src/libraries/Common/tests/TestUtilities/System/AssertExtensions.cs @@ -663,6 +663,126 @@ public ItemCount(int original, int remain) } } + static unsafe bool IsNegativeZero(float value) + { + return (*(uint*)(&value)) == 0x80000000; + } + + static unsafe bool IsPositiveZero(float value) + { + return (*(uint*)(&value)) == 0x00000000; + } + + static unsafe bool IsNegativeZero(double value) + { + return (*(ulong*)(&value)) == 0x8000000000000000; + } + + static unsafe bool IsPositiveZero(double value) + { + return (*(ulong*)(&value)) == 0x0000000000000000; + } + +#if NET6_0_OR_GREATER + static unsafe bool IsNegativeZero(Half value) + { + return (*(ushort*)(&value)) == 0x8000; + } + + static unsafe bool IsPositiveZero(Half value) + { + return (*(ushort*)(&value)) == 0x0000; + } +#endif + + // We have a custom ToString here to ensure that edge cases (specifically +-0.0, + // but also NaN and +-infinity) are correctly and consistently represented. + static string ToStringPadded(float value) + { + if (float.IsNaN(value)) + { + return "NaN".PadLeft(10); + } + else if (float.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(10); + } + else if (float.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(10); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(10); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(10); + } + else + { + return $"{value,10:G9}"; + } + } + + static string ToStringPadded(double value) + { + if (double.IsNaN(value)) + { + return "NaN".PadLeft(20); + } + else if (double.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(20); + } + else if (double.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(20); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(20); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(20); + } + else + { + return $"{value,20:G17}"; + } + } + +#if NET6_0_OR_GREATER + static string ToStringPadded(Half value) + { + if (Half.IsNaN(value)) + { + return "NaN".PadLeft(5); + } + else if (Half.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(5); + } + else if (Half.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(5); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(5); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(5); + } + else + { + return $"{value,5:G5}"; + } + } +#endif + /// Verifies that two values are equal, within the . /// The expected value /// The value to be compared against @@ -774,46 +894,6 @@ public static void Equal(double expected, double actual, double variance) { throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); } - - static unsafe bool IsNegativeZero(double value) - { - return (*(ulong*)(&value)) == 0x8000000000000000; - } - - static unsafe bool IsPositiveZero(double value) - { - return (*(ulong*)(&value)) == 0x0000000000000000; - } - - // We have a custom ToString here to ensure that edge cases (specifically +-0.0, - // but also NaN and +-infinity) are correctly and consistently represented. - static string ToStringPadded(double value) - { - if (double.IsNaN(value)) - { - return "NaN".PadLeft(20); - } - else if (double.IsPositiveInfinity(value)) - { - return "+\u221E".PadLeft(20); - } - else if (double.IsNegativeInfinity(value)) - { - return "-\u221E".PadLeft(20); - } - else if (IsNegativeZero(value)) - { - return "-0.0".PadLeft(20); - } - else if (IsPositiveZero(value)) - { - return "+0.0".PadLeft(20); - } - else - { - return $"{value,20:G17}"; - } - } } /// Verifies that two values are equal, within the . @@ -927,46 +1007,6 @@ public static void Equal(float expected, float actual, float variance) { throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); } - - static unsafe bool IsNegativeZero(float value) - { - return (*(uint*)(&value)) == 0x80000000; - } - - static unsafe bool IsPositiveZero(float value) - { - return (*(uint*)(&value)) == 0x00000000; - } - - // We have a custom ToString here to ensure that edge cases (specifically +-0.0, - // but also NaN and +-infinity) are correctly and consistently represented. - static string ToStringPadded(float value) - { - if (float.IsNaN(value)) - { - return "NaN".PadLeft(10); - } - else if (float.IsPositiveInfinity(value)) - { - return "+\u221E".PadLeft(10); - } - else if (float.IsNegativeInfinity(value)) - { - return "-\u221E".PadLeft(10); - } - else if (IsNegativeZero(value)) - { - return "-0.0".PadLeft(10); - } - else if (IsPositiveZero(value)) - { - return "+0.0".PadLeft(10); - } - else - { - return $"{value,10:G9}"; - } - } } #if NET6_0_OR_GREATER @@ -1081,46 +1121,73 @@ public static void Equal(Half expected, Half actual, Half variance) { throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); } + } +#endif - static unsafe bool IsNegativeZero(Half value) + /// Verifies that two values's binary representations are identical. + /// The expected value + /// The value to be compared against + /// Thrown when the representations are not identical + public static void Equal(double expected, double actual) + { + if (BitConverter.DoubleToInt64Bits(expected) == BitConverter.DoubleToInt64Bits(actual)) { - return (*(ushort*)(&value)) == 0x8000; + return; } - static unsafe bool IsPositiveZero(Half value) + if (PlatformDetection.IsRiscV64Process && double.IsNaN(expected) && double.IsNaN(actual)) { - return (*(ushort*)(&value)) == 0x0000; + // RISC-V does not preserve payload + return; } - // We have a custom ToString here to ensure that edge cases (specifically +-0.0, - // but also NaN and +-infinity) are correctly and consistently represented. - static string ToStringPadded(Half value) + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + + /// Verifies that two values's binary representations are identical. + /// The expected value + /// The value to be compared against + /// Thrown when the representations are not identical + public static void Equal(float expected, float actual) + { + static unsafe int SingleToInt32Bits(float value) { - if (Half.IsNaN(value)) - { - return "NaN".PadLeft(5); - } - else if (Half.IsPositiveInfinity(value)) - { - return "+\u221E".PadLeft(5); - } - else if (Half.IsNegativeInfinity(value)) - { - return "-\u221E".PadLeft(5); - } - else if (IsNegativeZero(value)) - { - return "-0.0".PadLeft(5); - } - else if (IsPositiveZero(value)) - { - return "+0.0".PadLeft(5); - } - else - { - return $"{value,5:G5}"; - } + return *(int*)&value; + } + + if (SingleToInt32Bits(expected) == SingleToInt32Bits(actual)) + { + return; + } + + if (PlatformDetection.IsRiscV64Process && float.IsNaN(expected) && float.IsNaN(actual)) + { + // RISC-V does not preserve payload + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); + } + +#if NET6_0_OR_GREATER + /// Verifies that two values's binary representations are identical. + /// The expected value + /// The value to be compared against + /// Thrown when the representations are not identical + public static void Equal(Half expected, Half actual) + { + if (BitConverter.HalfToInt16Bits(expected) == BitConverter.HalfToInt16Bits(actual)) + { + return; } + + if (PlatformDetection.IsRiscV64Process && Half.IsNaN(expected) && Half.IsNaN(actual)) + { + // RISC-V does not preserve payload + return; + } + + throw EqualException.ForMismatchedValues(ToStringPadded(expected), ToStringPadded(actual)); } #endif } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs index 1e7f06602c0ec..600326b39d85f 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs @@ -411,7 +411,7 @@ public static IEnumerable ExplicitConversion_ToSingle_TestData() public static void ExplicitConversion_ToSingle(Half value, float expected) // Check the underlying bits for verifying NaNs { float f = (float)value; - Assert.Equal(BitConverter.SingleToInt32Bits(expected), BitConverter.SingleToInt32Bits(f)); + AssertExtensions.Equal(expected, f); } public static IEnumerable ExplicitConversion_ToDouble_TestData() @@ -466,7 +466,7 @@ public static IEnumerable ExplicitConversion_ToDouble_TestData() public static void ExplicitConversion_ToDouble(Half value, double expected) // Check the underlying bits for verifying NaNs { double d = (double)value; - Assert.Equal(BitConverter.DoubleToInt64Bits(expected), BitConverter.DoubleToInt64Bits(d)); + AssertExtensions.Equal(expected, d); } // ---------- Start of To-half conversion tests ---------- @@ -554,7 +554,7 @@ public static IEnumerable ExplicitConversion_FromSingle_TestData() public static void ExplicitConversion_FromSingle(float f, Half expected) // Check the underlying bits for verifying NaNs { Half h = (Half)f; - Assert.Equal(BitConverter.HalfToUInt16Bits(expected), BitConverter.HalfToUInt16Bits(h)); + AssertExtensions.Equal(expected, h); } public static IEnumerable ExplicitConversion_FromDouble_TestData() @@ -648,7 +648,7 @@ public static IEnumerable ExplicitConversion_FromDouble_TestData() public static void ExplicitConversion_FromDouble(double d, Half expected) // Check the underlying bits for verifying NaNs { Half h = (Half)d; - Assert.Equal(BitConverter.HalfToUInt16Bits(expected), BitConverter.HalfToUInt16Bits(h)); + AssertExtensions.Equal(expected, h); } public static IEnumerable Parse_Valid_TestData() @@ -1105,7 +1105,7 @@ public static void ToStringRoundtrip(object o_value) { float value = o_value is float floatValue ? floatValue : (float)(Half)o_value; Half result = Half.Parse(value.ToString()); - Assert.Equal(BitConverter.HalfToUInt16Bits((Half)value), BitConverter.HalfToUInt16Bits(result)); + AssertExtensions.Equal((Half)value, result); } [Theory] @@ -1114,7 +1114,7 @@ public static void ToStringRoundtrip_R(object o_value) { float value = o_value is float floatValue ? floatValue : (float)(Half)o_value; Half result = Half.Parse(value.ToString("R")); - Assert.Equal(BitConverter.HalfToUInt16Bits((Half)value), BitConverter.HalfToUInt16Bits(result)); + AssertExtensions.Equal((Half)value, result); } public static IEnumerable RoundTripFloat_CornerCases()