Description
The built in floating point types compare NaN
as unequal when using ==
and !=
(following IEEE semantics) but compare it as equal when using the Equals
method.
Your floating point based types currently use IEEE semantics even for Equals
. I suggest using the same behaviour as the built in types in your floating point based types like vectors or quaternions.
The MSDN documentation of Equals
contains an exception that allows A.Equals(A)
to return false on floating point types, so you don't strictly violate its contract.
But returning false still breaks hash tables and does not match the behaviour of the built in types, so I consider it a bad idea.
This can be avoided by calling Equals
on the members instead of ==
in the implementation of Equals
but not in ==
and !=
.
For example with quaternion,
replace
public bool Equals(Quaternion other)
{
return (X == other.X &&
Y == other.Y &&
Z == other.Z &&
W == other.W);
}
with
public bool Equals(Quaternion other)
{
return (X.Equals(other.X) &&
Y.Equals(other.Y) &&
Z.Equals(other.Z) &&
W.Equals(other.W));
}
You might want to add tests that check that ==
and !=
compare all the above cases as unequal, so that they match the IEEE specification.
Replace:
// 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));
with:
// Equals does not follow IEEE semantics since many consumers rely on equality being reflexive.
// This includes collections like `Dictionary<K,V>` or `HashSet<T>`
Assert.True(a.Equals(a));
Assert.True(b.Equals(b));
Assert.True(c.Equals(c));
Assert.True(d.Equals(d));
// Counterintuitive result - IEEE rules for NaN comparison are weird!
Assert.False(a == a);
Assert.False(b == b);
Assert.False(c == c);
Assert.False(d == d);
Assert.True(a != a);
Assert.True(b != b);
Assert.True(c != c);
Assert.True(d != d);