Fixed NaN's GetHashCode and Equals invariant issue. (#16551)
authorJan Kotas <jkotas@microsoft.com>
Sun, 25 Feb 2018 18:30:31 +0000 (10:30 -0800)
committerGitHub <noreply@github.com>
Sun, 25 Feb 2018 18:30:31 +0000 (10:30 -0800)
Fix #6237

src/mscorlib/shared/System/Double.cs
src/mscorlib/shared/System/Single.cs

index 3652963..808f626 100644 (file)
@@ -17,6 +17,8 @@ using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Versioning;
 
+using Internal.Runtime.CompilerServices;
+
 namespace System
 {
     [Serializable]
@@ -221,16 +223,19 @@ namespace System
         //The hashcode for a double is the absolute value of the integer representation
         //of that double.
         //
-        public unsafe override int GetHashCode()
+        [MethodImpl(MethodImplOptions.AggressiveInlining)] // 64-bit constants make the IL unusually large that makes the inliner to reject the method
+        public override int GetHashCode()
         {
-            double d = m_value;
-            if (d == 0)
+            var bits = Unsafe.As<double, long>(ref m_value);
+
+            // Optimized check for IsNan() || IsZero()
+            if (((bits - 1) & 0x7FFFFFFFFFFFFFFF) >= 0x7FF0000000000000)
             {
-                // Ensure that 0 and -0 have the same hash code
-                return 0;
+                // Ensure that all NaNs and both zeros have the same hash code
+                bits &= 0x7FF0000000000000;
             }
-            long value = *(long*)(&d);
-            return unchecked((int)value) ^ ((int)(value >> 32));
+
+            return unchecked((int)bits) ^ ((int)(bits >> 32));
         }
 
         public override String ToString()
index df97427..7bffa1a 100644 (file)
@@ -16,6 +16,8 @@ using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Versioning;
 
+using Internal.Runtime.CompilerServices;
+
 namespace System
 {
     [Serializable]
@@ -213,16 +215,18 @@ namespace System
             return IsNaN(obj) && IsNaN(m_value);
         }
 
-        public unsafe override int GetHashCode()
+        public override int GetHashCode()
         {
-            float f = m_value;
-            if (f == 0)
+            var bits = Unsafe.As<float, int>(ref m_value);
+
+            // Optimized check for IsNan() || IsZero()
+            if (((bits - 1) & 0x7FFFFFFF) >= 0x7F800000)
             {
-                // Ensure that 0 and -0 have the same hash code
-                return 0;
+                // Ensure that all NaNs and both zeros have the same hash code
+                bits &= 0x7F800000;
             }
-            int v = *(int*)(&f);
-            return v;
+
+            return bits;
         }
 
         public override String ToString()