Skip to content

Equals with NaN values (IEEE vs. reflexivity) #13872

Closed
@CodesInChaos

Description

@CodesInChaos

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);

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions