Xeno raised the use of Double.compare to be used to test for equality of 
floating point values instead of ==.

This should only be done if -0.0 and 0.0 are considered different. If this is 
the case then the accompanying hashCode value should not use Double.hashCode 
since when two objects are equal they must have the same hash code. 

This is not the case for:

EpsilonDoublePrecisionContext
Vector1D
Vector2D
Vector3D
SphericalCoordinates
PolarCoordinates

When some of the numbers are 0.0 (or -0.0) as they use == for equality then 
their hash codes are different. In the cases of:

AffineTransformMatrix2D
AffineTransformMatrix3D
AxisAngleSequence

When the doubles are 1 ulp apart they are equal as they use Precision.equals 
for equality but they will have a different hash code. (Note that 
AffineTransformMatrix1D does not use Precision.equals but Double.compare == 0 
so is fine.)

I think I have found all the cases of a broken hashCode but a search should be 
made for Double.hashCode and check all the hashCode and equals implementations 
are consistent.


If Precision.equals is to be used in the equals method then it will require 
some changes to have a correct hashCode implementation. There are 3 options:

1. Document the inconsistency

In this case the class should be documented as being inconsistent with the 
hashCode contract, something like:

<p>Note: This class has a hashCode that is inconsistent with equals.


2. Switch to binary equality

Provide a binary equality in equals and then a second method for equals using a 
precision context.


3. Smarter hashCode functions

For the case where 0.0 == -0.0 is to be used in equals then the hash code can 
be produced using:

Double.hashCode(0.0 - x);

Since:

+0.0 - +0.0 = +0.0
+0.0 - -0.0 = +0.0

All other non-Nan values will have the sign inverted for the argument to 
hashCode and NaN will remain NaN.

For the cases using Precision, IIUC the Precision.equals is always used with an 
allowed 1 ulp difference. So the following would work:

Long.hashCode(Double.doubleToLongBits(0.0 - x) >>> 2);

This should discard the least significant 2 bits thus any differences of 1 ulp 
will be discarded from the number. Note I’ve not tested this approach.

Alex

Reply via email to