From 114ecad954cb91e63937cc47a91ac8549a971b0e Mon Sep 17 00:00:00 2001 From: Jan Ouwens Date: Wed, 19 Apr 2023 08:51:08 +0200 Subject: [PATCH] Adds explanation why you shouldn't use delta comparisons for doubles in equals methods --- ...-doesnt-use-doublecompare-for-field-foo.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/_errormessages/double-equals-doesnt-use-doublecompare-for-field-foo.md b/docs/_errormessages/double-equals-doesnt-use-doublecompare-for-field-foo.md index f3b0ef58e..5d6b580e6 100644 --- a/docs/_errormessages/double-equals-doesnt-use-doublecompare-for-field-foo.md +++ b/docs/_errormessages/double-equals-doesnt-use-doublecompare-for-field-foo.md @@ -4,3 +4,24 @@ title: "Double: equals doesn't use Double.compare for field foo" You should never use `==` to compare doubles or floats in an `equals` method. Always use [`Double.compare`](http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#compare(double,%20double%29)) or [`Double.compareTo`](http://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#compareTo(java.lang.Double%29)) instead. Josh Bloch explains this in his book Effective Java. The short summary is that this method will do the right thing when confronted with `NaN` and positive and negative infinities. For example, `Float.NaN` is not equal to itself, but it has to be for `equals`, or you would never be able to retrieve it from a `HashMap`. + +Should you use a delta function, such as `Math.abs(this.d - that.d) <= 0.0000001` to compare the values? In general, when comparing doubles, one should definitely do that, to avoid issues concerning rounding errors. In the case of `equals`, however, you will run into transitivity issues: + +{% highlight java %} +double number = 0.0000001; +double delta = 2 * number; + +double a = 1.0; +double b = 1.0 + number; +double c = 1.0 + number + number; + +Math.abs(a - b) <= delta == true +Math.abs(b - c) <= delta == true +Math.abs(a - c) <= delta == false // violates transitivity +{% endhighlight %} + +According the the transitivity requirement of `equals`, if `a.equals(b)` and `b.equals(c)`, `a.equals(c)` should also be true, which isn't the case when comparing deltas like this. + +You will run into a similar problem when defining `hashCodes`: if two objects are equal to each other, their `hashCode`s must be equal as well. The only way to make this work, is to return a constant `hashCode`, which is undesirable. + +Therefore, while you should in principle always compare doubles using a delta function, `equals` methods are an exception to this, and you should stick to using `Double.compare`.