What's Wrong With Using == to Compare Floats in Java

Why is comparing floats inconsistent in Java?

The difference is that 6.5 can be represented exactly in both float and double, whereas 3.2 can't be represented exactly in either type. and the two closest approximations are different.

An equality comparison between float and double first converts the float to a double and then compares the two. So the data loss.


You shouldn't ever compare floats or doubles for equality; because you can't really guarantee that the number you assign to the float or double is exact.

This rounding error is a characteristic feature of floating-point computation.

Squeezing infinitely many real numbers into a finite number of bits
requires an approximate representation. Although there are infinitely
many integers, in most programs the result of integer computations can
be stored in 32 bits.

In contrast, given any fixed number of bits,
most calculations with real numbers will produce quantities that
cannot be exactly represented using that many bits. Therefore the
result of a floating-point calculation must often be rounded in order
to fit back into its finite representation. This rounding error is the
characteristic feature of floating-point computation.

Check What Every Computer Scientist Should Know About Floating-Point Arithmetic for more!

Is comparing two same literal float numbers for equality wrong?

There's a rule of thumb that you should apply to all programming rules of thumb (rule of thumbs?):

They are oversimplified, and will result in boneheaded decision making if pushed too far. IF you do not fully -grok- the intent behind the rule of thumb, you will mess up. Perhaps the rule of thumb remains a net positive (applying it without thought will improve things more than it will make them worse), but it will cause damage, and in any case it cannot be used as an argument in a debate.

So, with that in mind, clearly, there is no point in asking the question:

"Giving that the rule of thumb 'do not use == to compare floats' exists, is it ALWAYS bad?".

The answer is the extremely obvious: Duh, no. It's not ALWAYS bad, because rules of thumb pretty much by definition, if not by common sense, never ALWAYS apply.

So let's break it down then.

WHY is there a rule of thumb that you shouldn't == compare floats?

Your question suggests you already know this: It's because doing any math on floating points as represented by IEEE754 concepts such as java's double or float are inexact (vs. concepts like java's BigDecimal, which is exact *).

Do what you should always do when faced with a rule of thumb that, upon grokking why the rule of thumb exists and realizing it does not apply to your scenario: Completely ignore it.

Perhaps your question boils down to: I THINK I grok the rule of thumb, but perhaps I'm missing something; aside from the 'floating point math introduces small deviations which mess up == comparison', which does not apply to this case, are there any other reasons for this rule of thumb that I am not aware of?

In which case, my answer is: As far as I know, no.

*) But BigDecimal has its own equality problems, such as: Are two BigDecimal objects that represent the same mathematical number precisely, but which are configured to render at a different scale 'equal'? That depends on whether your viewpoint is that they are numbers or objects representing an exact decimal point number along with some meta properties including how to render it and how to round things if explicitly asked to do so. For what it is worth, the equals implementation of BD, which has to make a sophie's choice and choose between 2 equally valid interpretations of what equality means, chooses 'I represent a number', not 'I represent a number along with a bunch of metadata'. The same sophie's choice exists in all JPA/Hibernate stacks: Does a JPA object represent 'a row in the database' (thus equality being defined solely by the primary key value, and if not saved yet, two objects cannot be equal, not even to itself, unless the same reference identity), or does it represent the thing that the row represents, e.g. a student, and not 'a row in the DB that represents a student', in which case unid is the one field that does NOT matter for identity, and all the others (name, birthdate, social security number, etc) do. equality is hard.

comparing float/double values using == operator

IBM has a recommendation for comparing two floats, using division rather than subtraction - this makes it easier to select an epsilon that works for all ranges of input.

if (abs(a/b - 1) < epsilon)

As for the value of epsilon, I would use 5.96e-08 as given in this Wikipedia table, or perhaps 2x that value.

Is Float.equals (nearly) completely useless and what should I use instead?

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.

Is it safe to compare a float and an int in Java?

Yes, your specific example is fine, since both 0 and 1 can be exactly represented as a float.

Note that this is not true in general: there are many large int values that's can't be represented exactly as a float. For example, the following prints out true (Ideone) even though 2_000_000_001 does not equal 2_000_000_000:

import java.util.*;
import java.lang.*;
import java.io.*;

class FloatTest {

private static boolean isTwoBillion(float f) {
return f == 2_000_000_000;
}

public static void main (String[] args) {
System.out.println(isTwoBillion(2_000_000_001));
}

}

Note that, unlike float, double has wide enough mantissa to store every 32-bit int value.

However, there are long values that cannot be represented as doubles. This starts occurring at long value 9_223_372_036_854_764 which is Long.MAX_VALUE/1000 - 10. The number one greater (...765) has no double counterpart, while the following number (...766) does. Put another way, starting with ...764 as a double, incrementing the last bit of the mantissa gives ...766 when converted back to long.

Why do they use >, <, and == in compare(float float1, float float2) in Java?

Comparing floats with == is not "incorrect". It just might produce confusing results for someone not familiar with floating point numbers.

In most real word scenarios == has the desired behavior. Usually, you do not care that the numbers are "almost" equal. You want perfect equality, because you are checking some corner case.

Float.compare primarily defines an order not equality. Bigger numbers should always be after smaller - no matter how small the difference. You don't want to end up with sorted array: [0.000002, 0.000001, 0.000003]. Otherwise, most algorithms (for example binary search) would produce garbage results.

In cases where you want equality within a limit, the limit is some defined property like line width, brush thickness or hit box size. In such cases you have to compare the difference against that property rather than hardcoded epsilon. It might have sense to have Float.compare(a,b,epsilon) method. It's just not very useful.



Related Topics



Leave a reply



Submit