The bug is in the following two lines of System.ValueType
: (I stepped into the reference source)
if (CanCompareBits(this))
return FastEqualsCheck(thisObj, obj);
(Both methods are [MethodImpl(MethodImplOptions.InternalCall)]
)
When all of the fields are 8 bytes wide, CanCompareBits
mistakenly returns true, resulting in a bitwise comparison of two different, but semantically identical, values.
When at least one field is not 8 bytes wide, CanCompareBits
returns false, and the code proceeds to use reflection to loop over the fields and call Equals
for each value, which correctly treats -0.0
as equal to 0.0
.
Here is the source for CanCompareBits
from SSCLI:
FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
{
WRAPPER_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
_ASSERTE(obj != NULL);
MethodTable* mt = obj->GetMethodTable();
FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked());
}
FCIMPLEND
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…