Ensure the Equals instance method for the various vector types is correct (#68691)
authorTanner Gooding <tagoo@outlook.com>
Fri, 29 Apr 2022 17:53:48 +0000 (10:53 -0700)
committerGitHub <noreply@github.com>
Fri, 29 Apr 2022 17:53:48 +0000 (10:53 -0700)
* Adding basic tests validating NaN.Equals(NaN) returns true for various vector types

* Updating the various vector APIs to use .NET compliant equality for the Equals method

* Fixing the Plane and Quaternion GetHashCode tests

* Removing JIT intrinsic support for the Equals instance method

* Accelerating the Equals instance method for the various vector types

25 files changed:
src/coreclr/jit/simdashwintrinsiclistarm64.h
src/coreclr/jit/simdashwintrinsiclistxarch.h
src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs
src/libraries/System.Numerics.Vectors/tests/Matrix3x2Tests.cs
src/libraries/System.Numerics.Vectors/tests/Matrix4x4Tests.cs
src/libraries/System.Numerics.Vectors/tests/PlaneTests.cs
src/libraries/System.Numerics.Vectors/tests/QuaternionTests.cs
src/libraries/System.Numerics.Vectors/tests/Vector2Tests.cs
src/libraries/System.Numerics.Vectors/tests/Vector3Tests.cs
src/libraries/System.Numerics.Vectors/tests/Vector4Tests.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Plane.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Quaternion.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector2.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector3.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Scalar.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs
src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs
src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs
src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs

index 382f783..848889b 100644 (file)
@@ -40,7 +40,6 @@
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Abs,                                                    1,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Abs,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector2,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector2_CreateBroadcast,                     NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Dot,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector64_Dot,                                NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector2,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector64_op_Equality,                        NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     get_One,                                                0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector2_get_One,                             NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     get_Zero,                                               0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector64_get_Zero,                           NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Max,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Max,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
@@ -61,7 +60,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector2,     SquareRoot,
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Abs,                                                    1,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Abs,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector3,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector3_CreateBroadcast,                     NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Dot,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_Dot,                               NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector3,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_op_Equality,                       NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     get_One,                                                0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector3_get_One,                             NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     get_Zero,                                               0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_get_Zero,                          NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Max,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Max,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
@@ -82,7 +80,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector3,     SquareRoot,
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Abs,                                                    1,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Abs,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector4,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector4_CreateBroadcast,                     NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Dot,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_Dot,                               NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector4,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_op_Equality,                       NI_Illegal},                                    SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     get_One,                                                0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector4_get_One,                             NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     get_Zero,                                               0,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_get_Zero,                          NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Max,                                                    2,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Max,                                 NI_Illegal},                                    SimdAsHWIntrinsicFlag::None)
@@ -114,7 +111,6 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128,  ConvertToUInt64,
 SIMD_AS_HWINTRINSIC_NM(VectorT128,  CreateBroadcast,            ".ctor",                    2,         {NI_VectorT128_CreateBroadcast,                 NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast,                  NI_VectorT128_CreateBroadcast},                 SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Dot,                                                    2,         {NI_Vector128_Dot,                              NI_Vector128_Dot,                               NI_Vector128_Dot,                               NI_Vector128_Dot,                               NI_Vector128_Dot,                               NI_Vector128_Dot,                               NI_Illegal,                                     NI_Illegal,                                     NI_Vector128_Dot,                               NI_Vector128_Dot},                              SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Equals,                                                 2,         {NI_AdvSimd_CompareEqual,                       NI_AdvSimd_CompareEqual,                        NI_AdvSimd_CompareEqual,                        NI_AdvSimd_CompareEqual,                        NI_AdvSimd_CompareEqual,                        NI_AdvSimd_CompareEqual,                        NI_AdvSimd_Arm64_CompareEqual,                  NI_AdvSimd_Arm64_CompareEqual,                  NI_AdvSimd_CompareEqual,                        NI_AdvSimd_Arm64_CompareEqual},                 SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT128,  EqualsInstance,             "Equals",                   2,         {NI_Vector128_op_Equality,                      NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality,                       NI_Vector128_op_Equality},                      SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Floor,                                                  1,         {NI_Illegal,                                    NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_Illegal,                                     NI_AdvSimd_Floor,                               NI_AdvSimd_Arm64_Floor},                        SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  get_AllBitsSet,                                         0,         {NI_Vector128_get_AllBitsSet,                   NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet,                    NI_Vector128_get_AllBitsSet},                   SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  get_Count,                                              0,         {NI_VectorT128_get_Count,                       NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count,                        NI_VectorT128_get_Count},                       SimdAsHWIntrinsicFlag::None)
index e407b6d..e6f3134 100644 (file)
@@ -40,7 +40,6 @@
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Abs,                                                    1,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector2_Abs,                             NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector2,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector2_CreateBroadcast,                 NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Dot,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_Dot,                           NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector2,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_op_Equality,                   NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     get_One,                                                0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector2_get_One,                         NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     get_Zero,                                               0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_get_Zero,                      NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector2,     Max,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_SSE_Max,                                 NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
@@ -61,7 +60,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector2,     SquareRoot,
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Abs,                                                    1,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector3_Abs,                             NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector3,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector3_CreateBroadcast,                 NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Dot,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_Dot,                           NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector3,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_op_Equality,                   NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     get_One,                                                0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector3_get_One,                         NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     get_Zero,                                               0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_get_Zero,                      NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector3,     Max,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_SSE_Max,                                 NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
@@ -82,7 +80,6 @@ SIMD_AS_HWINTRINSIC_ID(Vector3,     SquareRoot,
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Abs,                                                    1,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector4_Abs,                             NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_NM(Vector4,     CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector4_CreateBroadcast,                 NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Dot,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_Dot,                           NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector4,     EqualsInstance,             "Equals",                   2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_op_Equality,                   NI_Illegal},                                SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     get_One,                                                0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector4_get_One,                         NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     get_Zero,                                               0,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_get_Zero,                      NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(Vector4,     Max,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_SSE_Max,                                 NI_Illegal},                                SimdAsHWIntrinsicFlag::None)
@@ -114,7 +111,6 @@ SIMD_AS_HWINTRINSIC_ID(VectorT128,  ConvertToUInt64,
 SIMD_AS_HWINTRINSIC_NM(VectorT128,  CreateBroadcast,            ".ctor",                    2,         {NI_VectorT128_CreateBroadcast,             NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast,              NI_VectorT128_CreateBroadcast},             SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Dot,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Vector128_Dot,                           NI_Vector128_Dot,                           NI_VectorT128_Dot,                          NI_VectorT128_Dot,                          NI_Illegal,                                 NI_Illegal,                                 NI_Vector128_Dot,                           NI_Vector128_Dot},                          SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Equals,                                                 2,         {NI_SSE2_CompareEqual,                      NI_SSE2_CompareEqual,                       NI_SSE2_CompareEqual,                       NI_SSE2_CompareEqual,                       NI_SSE2_CompareEqual,                       NI_SSE2_CompareEqual,                       NI_VectorT128_Equals,                       NI_VectorT128_Equals,                       NI_SSE_CompareEqual,                        NI_SSE2_CompareEqual},                      SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT128,  EqualsInstance,             "Equals",                   2,         {NI_Vector128_op_Equality,                  NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality,                   NI_Vector128_op_Equality},                  SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  Floor,                                                  1,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_SSE41_Floor,                             NI_SSE41_Floor},                            SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  get_AllBitsSet,                                         0,         {NI_Vector128_get_AllBitsSet,               NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet,                NI_Vector128_get_AllBitsSet},               SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT128,  get_Count,                                              0,         {NI_VectorT128_get_Count,                   NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count,                    NI_VectorT128_get_Count},                   SimdAsHWIntrinsicFlag::None)
@@ -165,7 +161,6 @@ SIMD_AS_HWINTRINSIC_ID(VectorT256,  ConvertToUInt64,
 SIMD_AS_HWINTRINSIC_NM(VectorT256,  CreateBroadcast,            ".ctor",                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_VectorT256_CreateBroadcast,              NI_VectorT256_CreateBroadcast,              NI_VectorT256_CreateBroadcast,              NI_VectorT256_CreateBroadcast,              NI_VectorT256_CreateBroadcast,              NI_VectorT256_CreateBroadcast},             SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT256,  Dot,                                                    2,         {NI_Illegal,                                NI_Illegal,                                 NI_Vector256_Dot,                           NI_Vector256_Dot,                           NI_Vector256_Dot,                           NI_Vector256_Dot,                           NI_Illegal,                                 NI_Illegal,                                 NI_Vector256_Dot,                           NI_Vector256_Dot},                          SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT256,  Equals,                                                 2,         {NI_AVX2_CompareEqual,                      NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX2_CompareEqual,                       NI_AVX_CompareEqual,                        NI_AVX_CompareEqual},                       SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT256,  EqualsInstance,             "Equals",                   2,         {NI_Vector256_op_Equality,                  NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality,                   NI_Vector256_op_Equality},                  SimdAsHWIntrinsicFlag::InstanceMethod)
 SIMD_AS_HWINTRINSIC_ID(VectorT256,  Floor,                                                  1,         {NI_Illegal,                                NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_Illegal,                                 NI_AVX_Floor,                               NI_AVX_Floor},                              SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT256,  get_AllBitsSet,                                         0,         {NI_Vector256_get_AllBitsSet,               NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet,                NI_Vector256_get_AllBitsSet},               SimdAsHWIntrinsicFlag::None)
 SIMD_AS_HWINTRINSIC_ID(VectorT256,  get_Count,                                              0,         {NI_VectorT256_get_Count,                   NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count,                    NI_VectorT256_get_Count},                   SimdAsHWIntrinsicFlag::None)
index 3d44a1a..5d42375 100644 (file)
@@ -832,6 +832,20 @@ namespace System.Numerics.Tests
             Assert.False(Vector<T>.Zero.Equals(Vector<T>.One));
             Assert.False(Vector<T>.Zero.Equals(new Vector<T>(Util.One<T>())));
         }
+
+        [Fact]
+        public void VectorDoubleEqualsNaNTest()
+        {
+            var nan = new Vector<double>(double.NaN);
+            Assert.True(nan.Equals(nan));
+        }
+
+        [Fact]
+        public void VectorSingleEqualsNaNTest()
+        {
+            var nan = new Vector<float>(float.NaN);
+            Assert.True(nan.Equals(nan));
+        }
         #endregion
 
         #region System.Object Overloads
index ab87c0f..f4144ad 100644 (file)
@@ -983,7 +983,7 @@ namespace System.Numerics.Tests
 
         // A test for Matrix3x2 comparison involving NaN values
         [Fact]
-        public void Matrix3x2EqualsNanTest()
+        public void Matrix3x2EqualsNaNTest()
         {
             Matrix3x2 a = new Matrix3x2(float.NaN, 0, 0, 0, 0, 0);
             Matrix3x2 b = new Matrix3x2(0, float.NaN, 0, 0, 0, 0);
@@ -1020,13 +1020,12 @@ namespace System.Numerics.Tests
             Assert.False(e.IsIdentity);
             Assert.False(f.IsIdentity);
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
-            Assert.False(d.Equals(d));
-            Assert.False(e.Equals(e));
-            Assert.False(f.Equals(f));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
+            Assert.True(d.Equals(d));
+            Assert.True(e.Equals(e));
+            Assert.True(f.Equals(f));
         }
 
         // A test to make sure these types are blittable directly into GPU buffer memory layouts
index 79d6537..98bf54d 100644 (file)
@@ -2490,7 +2490,7 @@ namespace System.Numerics.Tests
 
         // A test for Matrix4x4 comparison involving NaN values
         [Fact]
-        public void Matrix4x4EqualsNanTest()
+        public void Matrix4x4EqualsNaNTest()
         {
             Matrix4x4 a = new Matrix4x4(float.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             Matrix4x4 b = new Matrix4x4(0, float.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -2577,23 +2577,22 @@ namespace System.Numerics.Tests
             Assert.False(o.IsIdentity);
             Assert.False(p.IsIdentity);
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
-            Assert.False(d.Equals(d));
-            Assert.False(e.Equals(e));
-            Assert.False(f.Equals(f));
-            Assert.False(g.Equals(g));
-            Assert.False(h.Equals(h));
-            Assert.False(i.Equals(i));
-            Assert.False(j.Equals(j));
-            Assert.False(k.Equals(k));
-            Assert.False(l.Equals(l));
-            Assert.False(m.Equals(m));
-            Assert.False(n.Equals(n));
-            Assert.False(o.Equals(o));
-            Assert.False(p.Equals(p));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
+            Assert.True(d.Equals(d));
+            Assert.True(e.Equals(e));
+            Assert.True(f.Equals(f));
+            Assert.True(g.Equals(g));
+            Assert.True(h.Equals(h));
+            Assert.True(i.Equals(i));
+            Assert.True(j.Equals(j));
+            Assert.True(k.Equals(k));
+            Assert.True(l.Equals(l));
+            Assert.True(m.Equals(m));
+            Assert.True(n.Equals(n));
+            Assert.True(o.Equals(o));
+            Assert.True(p.Equals(p));
         }
 
         // A test to make sure these types are blittable directly into GPU buffer memory layouts
index 22dc2eb..a078551 100644 (file)
@@ -107,7 +107,7 @@ namespace System.Numerics.Tests
         {
             Plane target = new Plane(1.0f, 2.0f, 3.0f, 4.0f);
 
-            int expected = target.Normal.GetHashCode() + target.D.GetHashCode();
+            int expected = HashCode.Combine(target.Normal, target.D);
             int actual = target.GetHashCode();
             Assert.Equal(expected, actual);
         }
@@ -286,7 +286,7 @@ namespace System.Numerics.Tests
 
         // A test for Plane comparison involving NaN values
         [Fact]
-        public void PlaneEqualsNanTest()
+        public void PlaneEqualsNaNTest()
         {
             Plane a = new Plane(float.NaN, 0, 0, 0);
             Plane b = new Plane(0, float.NaN, 0, 0);
@@ -308,11 +308,10 @@ namespace System.Numerics.Tests
             Assert.False(c.Equals(new Plane(0, 0, 0, 0)));
             Assert.False(d.Equals(new Plane(0, 0, 0, 0)));
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
-            Assert.False(d.Equals(d));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
+            Assert.True(d.Equals(d));
         }
 
         /* Enable when size of Vector3 is correct
index d573e0c..6bfaad2 100644 (file)
@@ -633,7 +633,7 @@ namespace System.Numerics.Tests
         {
             Quaternion a = new Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
 
-            int expected = unchecked(a.X.GetHashCode() + a.Y.GetHashCode() + a.Z.GetHashCode() + a.W.GetHashCode());
+            int expected = HashCode.Combine(a.X, a.Y, a.Z, a.W);
             int actual = a.GetHashCode();
             Assert.Equal(expected, actual);
         }
@@ -949,7 +949,7 @@ namespace System.Numerics.Tests
 
         // A test for Quaternion comparison involving NaN values
         [Fact]
-        public void QuaternionEqualsNanTest()
+        public void QuaternionEqualsNaNTest()
         {
             Quaternion a = new Quaternion(float.NaN, 0, 0, 0);
             Quaternion b = new Quaternion(0, float.NaN, 0, 0);
@@ -976,11 +976,10 @@ namespace System.Numerics.Tests
             Assert.False(c.IsIdentity);
             Assert.False(d.IsIdentity);
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
-            Assert.False(d.Equals(d));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
+            Assert.True(d.Equals(d));
         }
 
         // A test to make sure these types are blittable directly into GPU buffer memory layouts
index 13a4c44..2f07207 100644 (file)
@@ -1133,7 +1133,7 @@ namespace System.Numerics.Tests
 
         // A test for Vector2f comparison involving NaN values
         [Fact]
-        public void Vector2EqualsNanTest()
+        public void Vector2EqualsNaNTest()
         {
             Vector2 a = new Vector2(float.NaN, 0);
             Vector2 b = new Vector2(0, float.NaN);
@@ -1147,9 +1147,8 @@ namespace System.Numerics.Tests
             Assert.False(a.Equals(Vector2.Zero));
             Assert.False(b.Equals(Vector2.Zero));
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
         }
 
         // A test for Reflect (Vector2f, Vector2f)
index 7aa15e4..ae30f80 100644 (file)
@@ -1216,7 +1216,7 @@ namespace System.Numerics.Tests
 
         // A test for Vector3f comparison involving NaN values
         [Fact]
-        public void Vector3EqualsNanTest()
+        public void Vector3EqualsNaNTest()
         {
             Vector3 a = new Vector3(float.NaN, 0, 0);
             Vector3 b = new Vector3(0, float.NaN, 0);
@@ -1234,10 +1234,9 @@ namespace System.Numerics.Tests
             Assert.False(b.Equals(Vector3.Zero));
             Assert.False(c.Equals(Vector3.Zero));
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
         }
 
         [Fact]
index c5d2fa5..8de88a8 100644 (file)
@@ -1441,7 +1441,7 @@ namespace System.Numerics.Tests
 
         // A test for Vector4f comparison involving NaN values
         [Fact]
-        public void Vector4EqualsNanTest()
+        public void Vector4EqualsNaNTest()
         {
             Vector4 a = new Vector4(float.NaN, 0, 0, 0);
             Vector4 b = new Vector4(0, float.NaN, 0, 0);
@@ -1463,11 +1463,10 @@ namespace System.Numerics.Tests
             Assert.False(c.Equals(Vector4.Zero));
             Assert.False(d.Equals(Vector4.Zero));
 
-            // Counterintuitive result - IEEE rules for NaN comparison are weird!
-            Assert.False(a.Equals(a));
-            Assert.False(b.Equals(b));
-            Assert.False(c.Equals(c));
-            Assert.False(d.Equals(d));
+            Assert.True(a.Equals(a));
+            Assert.True(b.Equals(b));
+            Assert.True(c.Equals(c));
+            Assert.True(d.Equals(d));
         }
 
         [Fact]
index 156fc55..e56e41c 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
 
 namespace System.Numerics
 {
@@ -633,9 +634,30 @@ namespace System.Numerics
         /// <param name="other">The other matrix.</param>
         /// <returns><see langword="true" /> if the two matrices are equal; otherwise, <see langword="false" />.</returns>
         /// <remarks>Two matrices are equal if all their corresponding elements are equal.</remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Matrix3x2 other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
+            {
+                // We'll two two overlapping comparisons. The first gets M11, M12, M21, and M22
+                // The second will get M21, M22, M31, and M32. This is more efficient overall.
+
+                return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
+                    && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21));
+
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Matrix3x2 self, Matrix3x2 other)
+            {
+                return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) // Check diagonal element first for early out.
+                    && self.M12.Equals(other.M12) && self.M21.Equals(other.M21)
+                    && self.M31.Equals(other.M31) && self.M32.Equals(other.M32);
+            }
         }
 
         /// <summary>Calculates the determinant for this matrix.</summary>
index 5d57e13..fa0aea8 100644 (file)
@@ -2189,9 +2189,30 @@ namespace System.Numerics
         /// <summary>Returns a value that indicates whether this instance and another 4x4 matrix are equal.</summary>
         /// <param name="other">The other matrix.</param>
         /// <returns><see langword="true" /> if the two matrices are equal; otherwise, <see langword="false" />.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Matrix4x4 other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
+            {
+                return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
+                    && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21))
+                    && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M31)).Equals(Vector128.LoadUnsafe(ref other.M31))
+                    && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M41)).Equals(Vector128.LoadUnsafe(ref other.M41));
+
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Matrix4x4 self, Matrix4x4 other)
+            {
+                return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) && self.M33.Equals(other.M33) && self.M44.Equals(other.M44) // Check diagonal element first for early out.
+                    && self.M12.Equals(other.M12) && self.M13.Equals(other.M13) && self.M14.Equals(other.M14) && self.M21.Equals(other.M21)
+                    && self.M23.Equals(other.M23) && self.M24.Equals(other.M24) && self.M31.Equals(other.M31) && self.M32.Equals(other.M32)
+                    && self.M34.Equals(other.M34) && self.M41.Equals(other.M41) && self.M42.Equals(other.M42) && self.M43.Equals(other.M43);
+            }
         }
 
         /// <summary>Calculates the determinant of the current 4x4 matrix.</summary>
index 74f9b93..f60f32c 100644 (file)
@@ -4,6 +4,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
 
 namespace System.Numerics
 {
@@ -298,16 +299,20 @@ namespace System.Numerics
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Plane other)
         {
-            if (Vector.IsHardwareAccelerated)
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
             {
-                return Normal.Equals(other.Normal) && D == other.D;
+                return Vector128.LoadUnsafe(ref Unsafe.AsRef(in Normal.X)).Equals(Vector128.LoadUnsafe(ref other.Normal.X));
             }
-            else
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Plane self, Plane other)
             {
-                return (Normal.X == other.Normal.X &&
-                        Normal.Y == other.Normal.Y &&
-                        Normal.Z == other.Normal.Z &&
-                        D == other.D);
+                return self.Normal.Equals(other.Normal)
+                    && self.D.Equals(other.D);
             }
         }
 
@@ -315,7 +320,7 @@ namespace System.Numerics
         /// <returns>The hash code.</returns>
         public override readonly int GetHashCode()
         {
-            return Normal.GetHashCode() + D.GetHashCode();
+            return HashCode.Combine(Normal, D);
         }
 
         /// <summary>Returns the string representation of this plane object.</summary>
index 2dc01e1..c1f21dc 100644 (file)
@@ -4,6 +4,7 @@
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
 
 namespace System.Numerics
 {
@@ -668,16 +669,33 @@ namespace System.Numerics
         /// <param name="other">The other quaternion.</param>
         /// <returns><see langword="true" /> if the two quaternions are equal; otherwise, <see langword="false" />.</returns>
         /// <remarks>Two quaternions are equal if each of their corresponding components is equal.</remarks>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Quaternion other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
+            {
+                return Vector128.LoadUnsafe(ref Unsafe.AsRef(in X)).Equals(Vector128.LoadUnsafe(ref other.X));
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Quaternion self, Quaternion other)
+            {
+                return self.X.Equals(other.X)
+                    && self.Y.Equals(other.Y)
+                    && self.Z.Equals(other.Z)
+                    && self.W.Equals(other.W);
+            }
         }
 
         /// <summary>Returns the hash code for this instance.</summary>
         /// <returns>The hash code.</returns>
         public override readonly int GetHashCode()
         {
-            return unchecked(X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode());
+            return HashCode.Combine(X, Y, Z, W);
         }
 
         /// <summary>Calculates the length of the quaternion.</summary>
index ea98c48..774557a 100644 (file)
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
 using System.Text;
 
 namespace System.Numerics
@@ -641,10 +642,28 @@ namespace System.Numerics
         /// <param name="other">The other vector.</param>
         /// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
         /// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector2.X" /> and <see cref="System.Numerics.Vector2.Y" /> elements are equal.</remarks>
-        [Intrinsic]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Vector2 other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector64.IsHardwareAccelerated)
+            {
+                return Vector64.LoadUnsafe(ref Unsafe.AsRef(in X)).Equals(Vector64.LoadUnsafe(ref other.X));
+            }
+            else if (Vector128.IsHardwareAccelerated)
+            {
+                return this.AsVector128().Equals(other.AsVector128());
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Vector2 self, Vector2 other)
+            {
+                return self.X.Equals(other.X)
+                    && self.Y.Equals(other.Y);
+            }
         }
 
         /// <summary>Returns the hash code for this instance.</summary>
index ca4c737..1c62c2a 100644 (file)
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
 using System.Text;
 
 namespace System.Numerics
@@ -664,10 +665,25 @@ namespace System.Numerics
         /// <param name="other">The other vector.</param>
         /// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
         /// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector3.X" />, <see cref="System.Numerics.Vector3.Y" />, and <see cref="System.Numerics.Vector3.Z" /> elements are equal.</remarks>
-        [Intrinsic]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Vector3 other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
+            {
+                return this.AsVector128().Equals(other.AsVector128());
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Vector3 self, Vector3 other)
+            {
+                return self.X.Equals(other.X)
+                    && self.Y.Equals(other.Y)
+                    && self.Z.Equals(other.Z);
+            }
         }
 
         /// <summary>Returns the hash code for this instance.</summary>
index b7225e3..bd4645f 100644 (file)
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
 using System.Text;
 
 namespace System.Numerics
@@ -741,10 +742,26 @@ namespace System.Numerics
         /// <param name="other">The other vector.</param>
         /// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
         /// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector4.X" />, <see cref="System.Numerics.Vector4.Y" />, <see cref="System.Numerics.Vector4.Z" />, and <see cref="System.Numerics.Vector4.W" /> elements are equal.</remarks>
-        [Intrinsic]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public readonly bool Equals(Vector4 other)
         {
-            return this == other;
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector128.IsHardwareAccelerated)
+            {
+                return this.AsVector128().Equals(other.AsVector128());
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Vector4 self, Vector4 other)
+            {
+                return self.X.Equals(other.X)
+                    && self.Y.Equals(other.Y)
+                    && self.Z.Equals(other.Z)
+                    && self.W.Equals(other.W);
+            }
         }
 
         /// <summary>Returns a value that indicates whether this instance and a specified object are equal.</summary>
index 296f221..2622184 100644 (file)
@@ -587,9 +587,39 @@ namespace System.Numerics
         /// <summary>Returns a boolean indicating whether the given vector is equal to this vector instance.</summary>
         /// <param name="other">The vector to compare this instance to.</param>
         /// <returns>True if the other vector is equal to this instance; False otherwise.</returns>
-        [Intrinsic]
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool Equals(Vector<T> other)
-            => this == other;
+        {
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector.IsHardwareAccelerated)
+            {
+                if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
+                {
+                    Vector<T> result = Vector.Equals(this, other) | ~(Vector.Equals(this, this) | Vector.Equals(other, other));
+                    return result.As<T, int>() == Vector<int>.AllBitsSet;
+                }
+                else
+                {
+                    return this == other;
+                }
+            }
+
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Vector<T> self, Vector<T> other)
+            {
+                for (int index = 0; index < Count; index++)
+                {
+                    if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
 
         /// <summary>Returns the hash code for this instance.</summary>
         /// <returns>The hash code.</returns>
index b7a5ba8..4046064 100644 (file)
@@ -736,6 +736,62 @@ namespace System.Runtime.Intrinsics
             }
         }
 
+        public static bool ObjectEquals(T left, T right)
+        {
+            if (typeof(T) == typeof(byte))
+            {
+                return ((byte)(object)left).Equals((byte)(object)right);
+            }
+            else if (typeof(T) == typeof(double))
+            {
+                return ((double)(object)left).Equals((double)(object)right);
+            }
+            else if (typeof(T) == typeof(short))
+            {
+                return ((short)(object)left).Equals((short)(object)right);
+            }
+            else if (typeof(T) == typeof(int))
+            {
+                return ((int)(object)left).Equals((int)(object)right);
+            }
+            else if (typeof(T) == typeof(long))
+            {
+                return ((long)(object)left).Equals((long)(object)right);
+            }
+            else if (typeof(T) == typeof(nint))
+            {
+                return ((nint)(object)left).Equals((nint)(object)right);
+            }
+            else if (typeof(T) == typeof(nuint))
+            {
+                return ((nuint)(object)left).Equals((nuint)(object)right);
+            }
+            else if (typeof(T) == typeof(sbyte))
+            {
+                return ((sbyte)(object)left).Equals((sbyte)(object)right);
+            }
+            else if (typeof(T) == typeof(float))
+            {
+                return ((float)(object)left).Equals((float)(object)right);
+            }
+            else if (typeof(T) == typeof(ushort))
+            {
+                return ((ushort)(object)left).Equals((ushort)(object)right);
+            }
+            else if (typeof(T) == typeof(uint))
+            {
+                return ((uint)(object)left).Equals((uint)(object)right);
+            }
+            else if (typeof(T) == typeof(ulong))
+            {
+                return ((ulong)(object)left).Equals((ulong)(object)right);
+            }
+            else
+            {
+                throw new NotSupportedException(SR.Arg_TypeNotSupported);
+            }
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static T ShiftLeft(T value, int shiftCount)
         {
index 923a4b5..9ea29c7 100644 (file)
@@ -312,51 +312,33 @@ namespace System.Runtime.Intrinsics
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool Equals(Vector128<T> other)
         {
-            ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType<T>();
-
-            if (Sse.IsSupported && (typeof(T) == typeof(float)))
-            {
-                Vector128<float> result = Sse.CompareEqual(this.AsSingle(), other.AsSingle());
-                return Sse.MoveMask(result) == 0b1111; // We have one bit per element
-            }
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
 
-            if (Sse2.IsSupported)
+            if (Vector128.IsHardwareAccelerated)
             {
-                if (typeof(T) == typeof(double))
+                if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
                 {
-                    Vector128<double> result = Sse2.CompareEqual(this.AsDouble(), other.AsDouble());
-                    return Sse2.MoveMask(result) == 0b11; // We have one bit per element
-                }
-                else if (Sse41.IsSupported)
-                {
-                    // xor + testz is slightly better for integer types
-                    Vector128<byte> xored = Sse2.Xor(this.AsByte(), other.AsByte());
-                    return Sse41.TestZ(xored, xored);
+                    Vector128<T> result = Vector128.Equals(this, other) | ~(Vector128.Equals(this, this) | Vector128.Equals(other, other));
+                    return result.AsInt32() == Vector128<int>.AllBitsSet;
                 }
                 else
                 {
-                    // Unlike float/double, there are no special values to consider
-                    // for integral types and we can just do a comparison that all
-                    // bytes are exactly the same.
-
-                    Debug.Assert((typeof(T) != typeof(float)) && (typeof(T) != typeof(double)));
-                    Vector128<byte> result = Sse2.CompareEqual(this.AsByte(), other.AsByte());
-                    return Sse2.MoveMask(result) == 0b1111_1111_1111_1111; // We have one bit per element
+                    return this == other;
                 }
             }
 
             return SoftwareFallback(in this, other);
 
-            static bool SoftwareFallback(in Vector128<T> vector, Vector128<T> other)
+            static bool SoftwareFallback(in Vector128<T> self, Vector128<T> other)
             {
-                for (int i = 0; i < Count; i++)
+                for (int index = 0; index < Count; index++)
                 {
-                    if (!((IEquatable<T>)(vector.GetElement(i))).Equals(other.GetElement(i)))
+                    if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
                     {
                         return false;
                     }
                 }
-
                 return true;
             }
         }
index 0191741..0822fe5 100644 (file)
@@ -321,45 +321,33 @@ namespace System.Runtime.Intrinsics
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool Equals(Vector256<T> other)
         {
-            if (Avx.IsSupported)
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector256.IsHardwareAccelerated)
             {
-                if (typeof(T) == typeof(float))
+                if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
                 {
-                    Vector256<float> result = Avx.Compare(this.AsSingle(), other.AsSingle(), FloatComparisonMode.OrderedEqualNonSignaling);
-                    return Avx.MoveMask(result) == 0b1111_1111; // We have one bit per element
+                    Vector256<T> result = Vector256.Equals(this, other) | ~(Vector256.Equals(this, this) | Vector256.Equals(other, other));
+                    return result.AsInt32() == Vector256<int>.AllBitsSet;
                 }
-
-                if (typeof(T) == typeof(double))
+                else
                 {
-                    Vector256<double> result = Avx.Compare(this.AsDouble(), other.AsDouble(), FloatComparisonMode.OrderedEqualNonSignaling);
-                    return Avx.MoveMask(result) == 0b1111; // We have one bit per element
+                    return this == other;
                 }
             }
 
-            if (Avx2.IsSupported)
-            {
-                // Unlike float/double, there are no special values to consider
-                // for integral types and we can just do a comparison that all
-                // bytes are exactly the same.
-
-                Debug.Assert((typeof(T) != typeof(float)) && (typeof(T) != typeof(double)));
-
-                Vector256<byte> xored = Avx2.Xor(this.AsByte(), other.AsByte());
-                return Avx.TestZ(xored, xored);
-            }
-
             return SoftwareFallback(in this, other);
 
-            static bool SoftwareFallback(in Vector256<T> vector, Vector256<T> other)
+            static bool SoftwareFallback(in Vector256<T> self, Vector256<T> other)
             {
-                for (int i = 0; i < Count; i++)
+                for (int index = 0; index < Count; index++)
                 {
-                    if (!((IEquatable<T>)(vector.GetElement(i))).Equals(other.GetElement(i)))
+                    if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
                     {
                         return false;
                     }
                 }
-
                 return true;
             }
         }
index a53c36e..979ddb3 100644 (file)
@@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.Arm;
 using System.Text;
 
 namespace System.Runtime.Intrinsics
@@ -289,17 +290,38 @@ namespace System.Runtime.Intrinsics
         /// <param name="other">The <see cref="Vector64{T}" /> to compare with the current instance.</param>
         /// <returns><c>true</c> if <paramref name="other" /> is equal to the current instance; otherwise, <c>false</c>.</returns>
         /// <exception cref="NotSupportedException">The type of the current instance (<typeparamref name="T" />) is not supported.</exception>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool Equals(Vector64<T> other)
         {
-            for (int i = 0; i < Count; i++)
+            // This function needs to account for floating-point equality around NaN
+            // and so must behave equivalently to the underlying float/double.Equals
+
+            if (Vector64.IsHardwareAccelerated)
             {
-                if (!((IEquatable<T>)(this.GetElement(i))).Equals(other.GetElement(i)))
+                if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
                 {
-                    return false;
+                    Vector64<T> result = Vector64.Equals(this, other) | ~(Vector64.Equals(this, this) | Vector64.Equals(other, other));
+                    return result.AsInt32() == Vector64<int>.AllBitsSet;
+                }
+                else
+                {
+                    return this == other;
                 }
             }
 
-            return true;
+            return SoftwareFallback(in this, other);
+
+            static bool SoftwareFallback(in Vector64<T> self, Vector64<T> other)
+            {
+                for (int index = 0; index < Count; index++)
+                {
+                    if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
         }
 
         /// <summary>Gets the hash code for the instance.</summary>
index 67ab031..26942c5 100644 (file)
@@ -3844,5 +3844,19 @@ namespace System.Runtime.Intrinsics.Tests.Vectors
             Assert.Equal(vector[0], values[0]);
             Assert.Equal(vector[1], values[1]);
         }
+
+        [Fact]
+        public void Vector128DoubleEqualsNaNTest()
+        {
+            Vector128<double> nan = Vector128.Create(double.NaN);
+            Assert.True(nan.Equals(nan));
+        }
+
+        [Fact]
+        public void Vector128SingleEqualsNaNTest()
+        {
+            Vector128<float> nan = Vector128.Create(float.NaN);
+            Assert.True(nan.Equals(nan));
+        }
     }
 }
index 13727ba..0c4aa0a 100644 (file)
@@ -4707,5 +4707,19 @@ namespace System.Runtime.Intrinsics.Tests.Vectors
             Assert.Equal(vector[2], values[2]);
             Assert.Equal(vector[3], values[3]);
         }
+
+        [Fact]
+        public void Vector256DoubleEqualsNaNTest()
+        {
+            Vector256<double> nan = Vector256.Create(double.NaN);
+            Assert.True(nan.Equals(nan));
+        }
+
+        [Fact]
+        public void Vector256SingleEqualsNaNTest()
+        {
+            Vector256<float> nan = Vector256.Create(float.NaN);
+            Assert.True(nan.Equals(nan));
+        }
     }
 }
index 30297c8..b8a3f95 100644 (file)
@@ -3445,5 +3445,19 @@ namespace System.Runtime.Intrinsics.Tests.Vectors
 
             Assert.Equal(vector[0], values[0]);
         }
+
+        [Fact]
+        public void Vector64DoubleEqualsNaNTest()
+        {
+            Vector64<double> nan = Vector64.Create(double.NaN);
+            Assert.True(nan.Equals(nan));
+        }
+
+        [Fact]
+        public void Vector64SingleEqualsNaNTest()
+        {
+            Vector64<float> nan = Vector64.Create(float.NaN);
+            Assert.True(nan.Equals(nan));
+        }
     }
 }