7

Considering that:

It would seem like Float.equals in its current form is almost completely useless.

Am I missing something, or are there times when it is appropriate to use Float.equals, except in the staggeringly rare case that you want to test for binary equality?

And if so, is it genuinely the done thing to roll your own identikit epsilon function (as recommended in the first link), or is there an existing wrapper for this staggeringly common operation?

Also, does Double/Float.compare suffer from the same issue, or is there a existing comparator that takes an epsilon?

(Note that I can't change the existing libraries from Floats to BigD)

Community
  • 1
  • 1
deworde
  • 2,679
  • 5
  • 32
  • 60
  • may be duplicate with http://stackoverflow.com/questions/7668954/primitive-double-value-equal-depends-on-magnitude – Blip May 07 '15 at 14:35
  • @Blip See part 2 of my question, as the recommendation in that link is to roll your own identikit static function, which will be identical to all the other static functions that perform this operation. This is the ultimate antithesis of Don't Repeat Yourself. – deworde May 07 '15 at 14:37
  • *"it appears from the docs that all that Double/Float.equals does is compare with =="* Check the docs again. They are actually different. For example `equals` returns true for NaNs while `==` does not. – Radiodef May 07 '15 at 14:44
  • @Radiodef I'm not sure "being wrong" makes it useful. (Or does 2.7 equalling NaN make some sense I'm not aware of)? – deworde May 07 '15 at 14:48
  • Think about the hypothetical behavior of `a.equals(a)` returning false. So, it's intended as a value comparison so collections behave correctly (e.g. http://ideone.com/f0ufkb). *"This definition allows hash tables to operate properly."* A somewhat narrow purpose, but not useless. – Radiodef May 07 '15 at 14:53
  • @Radiodef Oh, sorry, I misread that, I thought that "equals returns true for NaNs" meant in *all* cases (e.g. against anything), which seemed completely wrong. Okay, I can see that being an improvement, but I'm not sure that (1*3/9).equals(1/3) being (potentially) false is less intrinsically bad when dealing with a.equals(a). – deworde May 07 '15 at 15:26
  • *"I'm not sure that (1*3/9).equals(1/3) being (potentially) false is less intrinsically bad"* But you're thinking in terms of arithmetic which is 1. a false expectation as we know since floating point has error and 2. has issues WRT `equals` if we understand 1. E.g. suppose we have `double a = 0.9999, b = 1.0000, c = 1.0001;` Our epsilon test returns `test(a, b) == true` and `test(b, c) == true`. If `equals` were defined in terms of epsilon test then it would demand transitive `test(a, c) == true` but this may not be the case. Thus `equals` is defined in terms of value. – Radiodef May 07 '15 at 15:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/77213/discussion-between-deworde-and-radiodef). – deworde May 07 '15 at 15:37
  • I'm sorry, I don't have time right now. : ( I see what you're saying, my point is that the definition of `equals` needs to be well-defined. – Radiodef May 07 '15 at 15:38

1 Answers1

3

Float.equals is useless if you're sure to compare Floats yourself, but it also checks the type of the argument and is reflexive. Don't forget equals is automatically called in collections for example.

Here's the source code:

public boolean equals(Object obj) {
    return (obj instanceof Float)
           && (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}

This allows any instance of Float, including new Float("NaN"), to be equal to itself, which is part of the general contract of equals, and new Float("-0") to be different from new Float("0") which might be useful (and is consistent with hashCode).

As for the second part : there's not a lot of cases, when you deal with real problems, where your epsilon isn't related to some context or physical dimension (or you probably shouldn't be using Float but BigDecimal). Semantically, equality for floating point numbers doesn't really make sense. At best you're interested in distances.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Which is rather my point. If equals on Float is unreliable, then surely you can't rely on it within the collections either (e.g. you think that adding "9" and "18/2" into a set will detect a collision, but of course it wont'). – deworde May 07 '15 at 14:40
  • Well, `equals` *must* be implemented just in case, it doesn't mean you *should* usually use **computed** Float instances as keys. – Denys Séguret May 07 '15 at 14:42
  • @deworde That depends on your expectation of floating-point arithmetic. It's probably more generally the case that we shouldn't use Float/Double as keys in a HashMap/HashSet. – Radiodef May 07 '15 at 14:42
  • @dystroy For the second part, wouldn't it make sense to have a settable epsilon on Float in that case, or at least a function Math.arithmeticEquals(f1, f2, epsilon)? After all, that function is what literally what every answer on this suggests, why isn't it part of a library? – deworde May 07 '15 at 15:00
  • BigDecimal is a mandatory replacement of Float/Double when doing stuff like comparison (http://stackoverflow.com/questions/356807/java-double-comparison-epsilon) – superbob May 07 '15 at 15:12
  • 1
    @deworde Semantically, equality for floating point numbers doesn't really make sense. What you need is usually some distance, and the thresholds are really dependent of your problem and goals. – Denys Séguret May 07 '15 at 15:19
  • @superbob Which is arguably the answer "yes, Float.equals is so completely useless an entire class was created to replace it for the situations where you'd use it." Unfortunately, I'm stuck with the Floats I'm given, mandatory or no mandatory. – deworde May 07 '15 at 15:28
  • `Float` makes sense because you sometimes need a wrapper class for primitive numbers. And floats are terribly useful in many cases, even if checking whether two numbers are almost equal does rarely make sense. – Denys Séguret May 07 '15 at 15:30
  • @dystroy Sure "Float" is useful. "Float.equals", on the other hand. – deworde May 07 '15 at 15:36
  • @deworde You could have a utility method that takes floats, converts them to BigDecimal using a specified precision, and compares the BigDecimal. That way you would have a "good" replacement for Float.equals, using BigDecimal under the hood. The only difficulty of that approach is the definition of the precision : it has to be a parameter of the method, and the value must match a context (Financial => 2, ...). – superbob May 11 '15 at 09:17
  • @superbob: Isn't the actual problem there that the conversion to BigDecimal is unreliable with respect to the same issues as equality, so you might as well just apply the precision equals directly to the float? – deworde May 11 '15 at 20:29
  • Marking this one correct for the "equal to itself" part,. which is the most convincing reason for why equals is the way it is rather than "fixed". – deworde May 11 '15 at 20:30
  • @deworde, it depends on what you consider to be the real problem with float.equals. If it is the representation issue: `1.0 != 1.00`, the conversion to BigDecimal will clear that ambiguity because you will specify the exact number of decimals and then `BigDecimal(1.0, 2) == BigDecimal(1.00, 2)`. – superbob May 12 '15 at 07:15