Floating point equality
In working through some bugs in Harmony, one thing that's been consistent is my surprise. Floating point's special values slightly complicate things...- Negative Zero
- Floats and doubles have two distinct representations for nada:
0.0F
and
.-0.0F - NaN
- Rather than risking an ArithmeticException, floats model error values natively. There are multiple binary representations of NaN; use floatToRawIntBits() to see yours. Most developers will prefer floatToIntBits(), which returns the same bits for all NaNs.
There's also a variety of ways to store floating point values. Each representation has its own merits and drawbacks:
- Primitives
- Fast, memory efficient, and nonnull.
- In wrappers
- Classes like Float and Double can be used in collections. Wrapper variables are nullable and implement convenient APIs from Number and Comparable.
- In buffers
- FloatBuffer and DoubleBuffer store nonnull values in bulk. Buffers provide direct access to data in files and streams.
Unfortunately, the interplay between the values and their representation is imperfect. Consider how equality differs by value and representation.
Values Primitives ==
In wrappers Object.equals()
In buffers Buffer.equals()
-0.0, +0.0 equal not equal equal NaN, NaN not equal equal equal
This behaviour is inconsistent but I suspect the differences weren't introduced lightly. Primitive types need to work in expressions so that
5/0
doesn't equal 0/0
. But wrapper types need to work in collections so that Set.add()
is consistent with Set.contains()
.On their own, exotic values and exotic representations both make sense. But since complexity is the N×M product of the feature set, our result is inevitable pain. Would be language designers should prefer fewer representations and fewer special values.