diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp index a99aa28de2..07b4d21d26 100644 --- a/src/CLR/Core/CLR_RT_HeapBlock.cpp +++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp @@ -1,4 +1,4 @@ -// +// // Copyright (c) .NET Foundation and Contributors // Portions Copyright (c) Microsoft Corporation. All rights reserved. // See LICENSE file in the project root for full license information. @@ -1102,8 +1102,21 @@ CLR_UINT32 CLR_RT_HeapBlock::GetHashCode(CLR_RT_HeapBlock *ptr, bool fRecurse, C break; case DATATYPE_R4: - crc = (CLR_INT32)ptr->NumericByRef().u8.LL; + { + // ensure that NaN and both zeros have the same hash code + int signBit = __signbitd(ptr->NumericByRef().r4); + + if (__isnanf(ptr->NumericByRef().r4) || (signBit && ptr->NumericByRef().r4 == 0)) + { + crc = (CLR_INT32)(ptr->NumericByRef().u8.LL & 0x7FFFFFFF); + } + else + { + crc = (CLR_INT32)ptr->NumericByRef().u8.LL; + } + break; + } case DATATYPE_U8: crc = ((CLR_INT32)ptr->NumericByRef().u8.LL ^ (CLR_INT32)ptr->NumericByRef().u8.HH); @@ -1114,8 +1127,20 @@ CLR_UINT32 CLR_RT_HeapBlock::GetHashCode(CLR_RT_HeapBlock *ptr, bool fRecurse, C break; case DATATYPE_R8: - crc = ((CLR_INT32)ptr->NumericByRef().r8.LL ^ (CLR_INT32)ptr->NumericByRef().r8.HH); + { + // ensure that NaN and both zeros have the same hash code + int signBit = __signbitd((double)ptr->NumericByRef().r8); + + if (__isnand((double)ptr->NumericByRef().r8) || (signBit && (double)ptr->NumericByRef().r8 == 0)) + { + crc = (CLR_INT32)(ptr->NumericByRef().r8.LL ^ ((CLR_INT32)ptr->NumericByRef().r8.HH & 0x7FFFFFFF)); + } + else + { + crc = ((CLR_INT32)ptr->NumericByRef().r8.LL ^ (CLR_INT32)ptr->NumericByRef().r8.HH); + } break; + } case DATATYPE_CLASS: case DATATYPE_VALUETYPE: @@ -1336,6 +1361,29 @@ bool CLR_RT_HeapBlock::ObjectsEqual( return Compare_Values(pArgLeft, pArgRight, false) == 0; break; + // edge cases, in .NET a NaN is equal to another NaN + // https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-double-equals?WT.mc_id=DT-MVP-5004179#nan + case DATATYPE_R4: + if (__isnanf(pArgLeft.NumericByRefConst().r4) && __isnanf(pArgRight.NumericByRefConst().r4)) + { + return true; + } + else + { + return Compare_Values(pArgLeft, pArgRight, false) == 0; + } + break; + case DATATYPE_R8: + if (__isnand((double)pArgLeft.NumericByRefConst().r8) && __isnand((double)pArgRight.NumericByRefConst().r8)) + { + return true; + } + else + { + return Compare_Values(pArgLeft, pArgRight, false) == 0; + } + break; + case DATATYPE_BYREF: if (rightDataType == DATATYPE_OBJECT) { @@ -1655,9 +1703,9 @@ CLR_INT32 CLR_RT_HeapBlock::Compare_Values(const CLR_RT_HeapBlock &left, const C case DATATYPE_R4: // deal with special cases: - // return 0 if the numbers are unordered (either or both are NaN) + // return 1 if the numbers are unordered (either or both are NaN) // this is post processed in interpreter so '1' will turn into '0' - if (__isnand(left.NumericByRefConst().r4) && __isnand(right.NumericByRefConst().r4)) + if (__isnanf(left.NumericByRefConst().r4) || __isnanf(right.NumericByRefConst().r4)) { return 1; } @@ -1688,7 +1736,7 @@ CLR_INT32 CLR_RT_HeapBlock::Compare_Values(const CLR_RT_HeapBlock &left, const C case DATATYPE_R8: // deal with special cases: - // return 0 if the numbers are unordered (either or both are NaN) + // return 1 if the numbers are unordered (either or both are NaN) // this is post processed in interpreter so '1' will turn into '0' if (__isnand((double)left.NumericByRefConst().r8) || __isnand((double)right.NumericByRefConst().r8)) { diff --git a/src/PAL/Include/nanoPAL_NativeDouble.h b/src/PAL/Include/nanoPAL_NativeDouble.h index e7d29da7e9..0a48282157 100644 --- a/src/PAL/Include/nanoPAL_NativeDouble.h +++ b/src/PAL/Include/nanoPAL_NativeDouble.h @@ -1,4 +1,4 @@ -// +// // Copyright (c) .NET Foundation and Contributors // Portions Copyright (c) Microsoft Corporation. All rights reserved. // See LICENSE file in the project root for full license information. @@ -14,6 +14,7 @@ #include #define __isnand _isnan +#define __isnanf _isnan #define __isinfd(x) (!_finite(x)) inline int __signbitd(double x) @@ -23,7 +24,7 @@ inline int __signbitd(double x) } #define rint(x) floor((x) + 0.5) -#define remainder(x, y) ((x) - ((y)*rint((x) / (y)))) +#define remainder(x, y) ((x) - ((y) * rint((x) / (y)))) #define isgreater(param0, param1) (param0 > param1) #define isless(param0, param1) (param0 < param1)