Half conversion test cases for subnormal corner cases (#46523)
authorEmile Cormier <ecorm@users.noreply.github.com>
Mon, 4 Jan 2021 17:41:38 +0000 (13:41 -0400)
committerGitHub <noreply@github.com>
Mon, 4 Jan 2021 17:41:38 +0000 (09:41 -0800)
* Half conversion test cases for subnormal corner cases

* Single/Double->Half rounding corner test cases

* Single/Double->Half conversion subnormal rounding corner test cases

src/libraries/System.Runtime/tests/System/HalfTests.cs

index 6f75e6c..fecb2d3 100644 (file)
@@ -376,6 +376,16 @@ namespace System.Tests
                 (UInt16BitsToHalf(0b1_01111_1000000000), -1.5f), // -1.5
                 (UInt16BitsToHalf(0b0_01111_1000000001), 1.5009765625f), // 1.5009765625
                 (UInt16BitsToHalf(0b1_01111_1000000001), -1.5009765625f), // -1.5009765625
+                (UInt16BitsToHalf(0b0_00001_0000000000), BitConverter.Int32BitsToSingle(0x38800000)), // smallest normal
+                // (UInt16BitsToHalf(0b0_00000_1111111111), BitConverter.Int32BitsToSingle(0x387FC000)), // largest subnormal (fails)
+                // (UInt16BitsToHalf(0b0_00000_1000000000), BitConverter.Int32BitsToSingle(0x38000000)), // middle subnormal (fails)
+                (UInt16BitsToHalf(0b0_00000_0111111111), BitConverter.Int32BitsToSingle(0x37FF8000)), // just below middle subnormal
+                (UInt16BitsToHalf(0b0_00000_0000000001), BitConverter.Int32BitsToSingle(0x33800000)), // smallest subnormal
+                (UInt16BitsToHalf(0b1_00000_0000000001), BitConverter.Int32BitsToSingle(unchecked((int)0xB3800000))), // highest negative subnormal
+                (UInt16BitsToHalf(0b1_00000_0111111111), BitConverter.Int32BitsToSingle(unchecked((int)0xB7FF8000))), // just above negative middle subnormal
+                // (UInt16BitsToHalf(0b1_00000_1000000000), BitConverter.Int32BitsToSingle(unchecked((int)0xB8000000))), // negative middle subnormal (fails)
+                // (UInt16BitsToHalf(0b1_00000_1111111111), BitConverter.Int32BitsToSingle(unchecked((int)0xB87FC000))), // lowest negative subnormal (fails)
+                (UInt16BitsToHalf(0b1_00001_0000000000), BitConverter.Int32BitsToSingle(unchecked((int)0xB8800000))) // highest negative normal
             };
 
             foreach ((Half original, float expected) in data)
@@ -420,7 +430,17 @@ namespace System.Tests
                 (UInt16BitsToHalf(0b0_01111_1000000000), 1.5d), // 1.5
                 (UInt16BitsToHalf(0b1_01111_1000000000), -1.5d), // -1.5
                 (UInt16BitsToHalf(0b0_01111_1000000001), 1.5009765625d), // 1.5009765625
-                (UInt16BitsToHalf(0b1_01111_1000000001), -1.5009765625d) // -1.5009765625
+                (UInt16BitsToHalf(0b1_01111_1000000001), -1.5009765625d), // -1.5009765625
+                (UInt16BitsToHalf(0b0_00001_0000000000), BitConverter.Int64BitsToDouble(0x3F10000000000000)), // smallest normal
+                // (UInt16BitsToHalf(0b0_00000_1111111111), BitConverter.Int64BitsToDouble(0x3F0FF80000000000)), // largest subnormal (fails)
+                // (UInt16BitsToHalf(0b0_00000_1000000000), BitConverter.Int64BitsToDouble(0x3f00000000000000)), // middle subnormal (fails)
+                (UInt16BitsToHalf(0b0_00000_0111111111), BitConverter.Int64BitsToDouble(0x3EFFF00000000000)), // just below middle subnormal
+                (UInt16BitsToHalf(0b0_00000_0000000001), BitConverter.Int64BitsToDouble(0x3E70000000000000)), // smallest subnormal
+                (UInt16BitsToHalf(0b1_00000_0000000001), BitConverter.Int64BitsToDouble(unchecked((long)0xBE70000000000000))), // highest negative subnormal
+                (UInt16BitsToHalf(0b1_00000_0111111111), BitConverter.Int64BitsToDouble(unchecked((long)0xBEFFF00000000000))), // just above negative middle subnormal
+                // (UInt16BitsToHalf(0b1_00000_1000000000), BitConverter.Int64BitsToDouble(unchecked((long)0xBF00000000000000))), // negative middle subnormal (fails)
+                // (UInt16BitsToHalf(0b1_00000_1111111111), BitConverter.Int64BitsToDouble(unchecked((long)0xBF0FF80000000000))), // lowest negative subnormal (fails)
+                (UInt16BitsToHalf(0b1_00001_0000000000), BitConverter.Int64BitsToDouble(unchecked((long)0xBF10000000000000))) // highest negative normal
             };
 
             foreach ((Half original, double expected) in data)
@@ -469,6 +489,45 @@ namespace System.Tests
                 (-1.5f, UInt16BitsToHalf(0b1_01111_1000000000)), // -1.5
                 (1.5009765625f, UInt16BitsToHalf(0b0_01111_1000000001)), // 1.5009765625
                 (-1.5009765625f, UInt16BitsToHalf(0b1_01111_1000000001)), // -1.5009765625
+                (BitConverter.Int32BitsToSingle(0x38800000), UInt16BitsToHalf(0b0_00001_0000000000)), // smallest normal
+                (BitConverter.Int32BitsToSingle(0x387FC000), UInt16BitsToHalf(0b0_00000_1111111111)), // largest subnormal
+                (BitConverter.Int32BitsToSingle(0x38000000), UInt16BitsToHalf(0b0_00000_1000000000)), // middle subnormal
+                (BitConverter.Int32BitsToSingle(0x37FF8000), UInt16BitsToHalf(0b0_00000_0111111111)), // just below middle subnormal
+                (BitConverter.Int32BitsToSingle(0x33800000), UInt16BitsToHalf(0b0_00000_0000000001)), // smallest subnormal
+                (BitConverter.Int32BitsToSingle(unchecked((int)0xB3800000)),
+                    UInt16BitsToHalf(0b1_00000_0000000001)), // highest negative subnormal
+                (BitConverter.Int32BitsToSingle(unchecked((int)0xB7FF8000)),
+                    UInt16BitsToHalf(0b1_00000_0111111111)), // just above negative middle subnormal
+                (BitConverter.Int32BitsToSingle(unchecked((int)0xB8000000)),
+                    UInt16BitsToHalf(0b1_00000_1000000000)), // negative middle subnormal
+                (BitConverter.Int32BitsToSingle(unchecked((int)0xB87FC000)),
+                    UInt16BitsToHalf(0b1_00000_1111111111)), // lowest negative subnormal
+                (BitConverter.Int32BitsToSingle(unchecked((int)0xB8800000)),
+                    UInt16BitsToHalf(0b1_00001_0000000000)), // highest negative normal
+                (BitConverter.Int32BitsToSingle(0b0_10001001_00000000111000000000001),
+                                  UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5+ULP rounds up
+                (BitConverter.Int32BitsToSingle(0b0_10001001_00000000111000000000000),
+                                  UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5 rounds to even
+                (BitConverter.Int32BitsToSingle(0b0_10001001_00000000110111111111111),
+                                  UInt16BitsToHalf(0b0_11001_0000000011)), // 1027.5-ULP rounds down
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000110111111111111)),
+                                                 UInt16BitsToHalf(0b1_11001_0000000011)), // -1027.5+ULP rounds towards zero
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000111000000000000)),
+                                                 UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5 rounds to even
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_10001001_00000000111000000000001)),
+                                                 UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5-ULP rounds away from zero
+                (BitConverter.Int32BitsToSingle(0b0_01110000_00000001110000000000001),
+                                 UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal + ULP rounds up
+                (BitConverter.Int32BitsToSingle(0b0_01110000_00000001110000000000000),
+                                 UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal rounds to even
+                (BitConverter.Int32BitsToSingle(0b0_01110000_00000001101111111111111),
+                                 UInt16BitsToHalf(0b0_00000_1000000011)), // subnormal - ULP rounds down
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001101111111111111)),
+                                                UInt16BitsToHalf(0b1_00000_1000000011)), // neg subnormal + ULP rounds higher
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001110000000000000)),
+                                                UInt16BitsToHalf(0b1_00000_1000000100)), // neg subnormal rounds to even
+                (BitConverter.Int32BitsToSingle(unchecked((int)0b1_01110000_00000001101111111111111)),
+                                                UInt16BitsToHalf(0b1_00000_1000000011)) // neg subnormal - ULP rounds lower
             };
 
             foreach ((float original, Half expected) in data)
@@ -518,6 +577,50 @@ namespace System.Tests
                 (-1.5d, UInt16BitsToHalf(0b1_01111_1000000000)), // -1.5
                 (1.5009765625d, UInt16BitsToHalf(0b0_01111_1000000001)), // 1.5009765625
                 (-1.5009765625d, UInt16BitsToHalf(0b1_01111_1000000001)), // -1.5009765625
+                (BitConverter.Int64BitsToDouble(0x3F10000000000000),
+                    UInt16BitsToHalf(0b0_00001_0000000000)), // smallest normal
+                (BitConverter.Int64BitsToDouble(0x3F0FF80000000000),
+                    UInt16BitsToHalf(0b0_00000_1111111111)), // largest subnormal
+                (BitConverter.Int64BitsToDouble(0x3f00000000000000),
+                    UInt16BitsToHalf(0b0_00000_1000000000)), // middle subnormal
+                (BitConverter.Int64BitsToDouble(0x3EFFF00000000000),
+                    UInt16BitsToHalf(0b0_00000_0111111111)), // just below middle subnormal
+                (BitConverter.Int64BitsToDouble(0x3E70000000000000),
+                    UInt16BitsToHalf(0b0_00000_0000000001)), // smallest subnormal
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBE70000000000000)),
+                    UInt16BitsToHalf(0b1_00000_0000000001)), // highest negative subnormal
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBEFFF00000000000)),
+                    UInt16BitsToHalf(0b1_00000_0111111111)), // just above negative middle subnormal
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF00000000000000)),
+                    UInt16BitsToHalf(0b1_00000_1000000000)), // negative middle subnormal
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF0FF80000000000)),
+                    UInt16BitsToHalf(0b1_00000_1111111111)), // lowest negative subnormal
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF10000000000000)),
+                    UInt16BitsToHalf(0b1_00001_0000000000)), // highest negative normal
+                (BitConverter.Int64BitsToDouble(0x40900E0000000001),
+                    UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5+ULP rounds up
+                (BitConverter.Int64BitsToDouble(0x40900E0000000000),
+                    UInt16BitsToHalf(0b0_11001_0000000100)), // 1027.5 rounds to even
+                (BitConverter.Int64BitsToDouble(0x40900DFFFFFFFFFF),
+                    UInt16BitsToHalf(0b0_11001_0000000011)), // 1027.5-ULP rounds down
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900DFFFFFFFFFF)),
+                    UInt16BitsToHalf(0b1_11001_0000000011)), // -1027.5+ULP rounds towards zero
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900E0000000000)),
+                    UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5 rounds to even
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xC0900E0000000001)),
+                    UInt16BitsToHalf(0b1_11001_0000000100)), // -1027.5-ULP rounds away from zero
+                (BitConverter.Int64BitsToDouble(0x3F001C0000000001),
+                    UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal + ULP rounds up
+                (BitConverter.Int64BitsToDouble(0x3F001C0000000001),
+                    UInt16BitsToHalf(0b0_00000_1000000100)), // subnormal rounds to even
+                (BitConverter.Int64BitsToDouble(0x3F001BFFFFFFFFFF),
+                    UInt16BitsToHalf(0b0_00000_1000000011)), // subnormal - ULP rounds down
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF001BFFFFFFFFFF)),
+                    UInt16BitsToHalf(0b1_00000_1000000011)), // neg subnormal + ULP rounds higher
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF001C0000000000)),
+                    UInt16BitsToHalf(0b1_00000_1000000100)), // neg subnormal rounds to even
+                (BitConverter.Int64BitsToDouble(unchecked((long)0xBF001C0000000001)),
+                    UInt16BitsToHalf(0b1_00000_1000000100)) // neg subnormal - ULP rounds lower
             };
 
             foreach ((double original, Half expected) in data)
@@ -885,6 +988,17 @@ namespace System.Tests
             yield return new object[] { MathF.PI };
             yield return new object[] { Half.MaxValue };
             yield return new object[] { Half.PositiveInfinity };
+
+            yield return new object[] { (UInt16BitsToHalf(0b0_00001_0000000000))}; // smallest normal
+            yield return new object[] { (UInt16BitsToHalf(0b0_00000_1111111111))}; // largest subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b0_00000_1000000000))}; // middle subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b0_00000_0111111111))}; // just below middle subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b0_00000_0000000001))}; // smallest subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b1_00000_0000000001))}; // highest negative subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b1_00000_0111111111))}; // just above negative middle subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b1_00000_1000000000))}; // negative middle subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b1_00000_1111111111))}; // lowest negative subnormal
+            yield return new object[] { (UInt16BitsToHalf(0b1_00001_0000000000))}; // highest negative normal
         }
 
         [Theory]