Here is the example with comments:
class Program
{
// first version of structure
public struct D1
{
public double d;
public int f;
}
// during some changes in code then we got D2 from D1
// Field f type became double while it was int before
public struct D2
{
public double d;
public double f;
}
static void Main(string[] args)
{
// Scenario with the first version
D1 a = new D1();
D1 b = new D1();
a.f = b.f = 1;
a.d = 0.0;
b.d = -0.0;
bool r1 = a.Equals(b); // gives true, all is ok
// The same scenario with the new one
D2 c = new D2();
D2 d = new D2();
c.f = d.f = 1;
c.d = 0.0;
d.d = -0.0;
bool r2 = c.Equals(d); // false! this is not the expected result
}
}
So, what do you think about this?
Best Answer
The bug is in the following two lines of
System.ValueType
: (I stepped into the reference source)(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 callEquals
for each value, which correctly treats-0.0
as equal to0.0
.Here is the source for
CanCompareBits
from SSCLI: