When comparing floating point values for equality, there are two different approaches:
NaNnot being equal to itself, which matches the IEEE 754 specification.
NaNbeing equal to itself, which provides the mathematical property of Reflexivity which is essential to the definition of an Equivalence relation
The built in IEEE floating point types in C# (
double) follow IEEE semantics for
!= (and the relational operators like
<) but ensure reflexivity for
Now consider a library that provides vector structs on top of
double. Such a vector type would overload
!= and override
What everybody agrees on is that
!= should follow IEEE semantics. The question is, should such a library implement the
Equals method (which is separate from the equality operators) in a way that's reflexive or in a way that matches the IEEE semantics.
Arguments for using IEEE semantics for
- It follows IEEE 754
It's (possibly much) faster because it can take advantage of SIMD instructions
I've asked a separate question on stackoverflow about how you'd express reflexive equality using SIMD instructions and their performance impact: SIMD instructions for floating point equality comparison
Update: It seems like it's possible to implement reflexive equality efficiently using three SIMD instructions.
The documentation for
Equalsdoesn't require reflexivity when involving floating point:
The following statements must be true for all implementations of the Equals(Object) method. In the list,
zrepresent object references that are not null.
true, except in cases that involve floating-point types. See ISO/IEC/IEEE 60559:2011, Information technology -- Microprocessor Systems -- Floating-Point arithmetic.
If you're using floats as dictionary keys you're living in a state of sin and should not expect sane behaviour.
Arguments for being reflexive:
It's consistent with existing types, including
I don't know any precedent in the BCL where
Equalsfollows IEEE instead of being reflexive. Counter examples include
Equalsis mostly used by containers and search algorithms which rely on reflexivity. For these algorithms a performance gain is irrelevant if prevents them from working. Don't sacrifice correctness for performance.
- It breaks all hash based sets and dictionaries,
IndexOfon various collections/LINQ, set based LINQ operations (
Except, etc.) if the data contains
Code that does actual computations where IEEE semantic is acceptable usually works on concrete types and uses
!=(or more likely epsilon comparisons).
You currently can't write high performance computations using generics since you need arithmetic operations for that, but these aren't available through interfaces/virtual methods.
So a slower
Equalsmethod wouldn't affect most high performance code.
It's possible to provide an
IeeeEqualsmethod or an
IeeeEqualityComparer<T>for the cases where you either need the IEEE semantics or you need to performance advantage.
In my opinion these arguments strongly favour a reflexive implementation.
Microsoft's CoreFX team plans to introduce such a vector type in .NET. Unlike me they prefer the IEEE solution, mainly due to the performance advantages. Since such a decision certainly won't be changed after a final release, I want to get feedback from the community, on what I believe to be a big mistake.