I am overriding equals and hashCode in a class that includes double fields. My first approach was to use the epsilon test in the equals method, and Double.hashCode( double ) in hashCode, but that can result in equal objects having different hash codes; here is a simplified example:
public class DoubleHashTest2
{
public static void main(String[] args)
{
double base1 = .9;
double base2 = .7;
Test test1 = new Test( base1 - .1 );
Test test2 = new Test( base2 + .1 );
System.out.println( test1.equals( test2 ) );
System.out.println( test1.hashCode() );
System.out.println( test2.hashCode() );
}
private static class Test
{
private double dnum1;
public Test( double dnum1 )
{
this.dnum1 = dnum1;
}
public boolean equals( Test other )
{
final double epsilon = .0001;
boolean result = false;
if ( this == other )
result = true;
else if ( other == null )
result = false;
else
result = Math.abs( this.dnum1 - other.dnum1 ) < epsilon;
return result;
}
public int hashCode()
{
int hash = Double.hashCode( dnum1 );
return hash;
}
}
}
I've thought of several solutions, including converting to BigDecimal, but I'm not really happy with any of them. I finally settled on rounding:
public boolean equals( Test other )
{
boolean result = false;
if ( this == other )
result = true;
else if ( other == null )
result = false;
else
{
double test1 = round( dnum1 );
double test2 = round( other.dnum1 );
result = test1 == test2;
}
return result;
}
public int hashCode()
{
double temp = round( dnum1 );
int hash = Double.hashCode( temp );
return hash;
}
private double round( double dnum )
{
// tests for NaN and +/-infinity omitted for brevity
final int places = 4;
final double round_const = Math.pow( 10, places );
double result = ((int)(dnum * round_const + .5)) / round_const;
return result;
}
But choosing a good rounding algorithm is difficult, and this seems kind of expensive. I looked at similar classes, such as Point2D.Double, but equals in this class fails, for example, when comparing .8 and 0.7999999999999999.
Is there a recommended way for dealing with this issue?
You don't need any custom rounding, as Double
class has doubleToLongBits()
method, which simply converts double
to long
(both of them are 64-bit values).
Also, for your equals()
method, you can compare two double
values with Double#compare()
.
Possible equals()
and hashCode()
for your example:
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (null == other
|| this.getClass() != other.getClass()) {
return false;
}
return Double.compare(this.dnum1, ((Test) other).dnum1) == 0;
}
public int hashCode() {
long bits = Double.doubleToLongBits(this.dnum1);
return (int) (bits ^ (bits >>> 32));
}
Your example shows the disadvantage of double
usage for floating point calculations - even values with the same magnitude can give close, but different results. Maybe you should use BigDecimal?
Also, see this question answers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With