What's wrong with using == to compare floats in Java?
JavaEqualityFloating AccuracyJava Problem Overview
According to this java.sun page ==
is the equality comparison operator for floating point numbers in Java.
However, when I type this code:
if(sectionID == currentSectionID)
into my editor and run static analysis, I get: "JAVA0078 Floating point values compared with =="
What is wrong with using ==
to compare floating point values? What is the correct way to do it?
Java Solutions
Solution 1 - Java
the correct way to test floats for 'equality' is:
if(Math.abs(sectionID - currentSectionID) < epsilon)
where epsilon is a very small number like 0.00000001, depending on the desired precision.
Solution 2 - Java
Floating point values can be off by a little bit, so they may not report as exactly equal. For example, setting a float to "6.1" and then printing it out again, you may get a reported value of something like "6.099999904632568359375". This is fundamental to the way floats work; therefore, you don't want to compare them using equality, but rather comparison within a range, that is, if the diff of the float to the number you want to compare it to is less than a certain absolute value.
This article on the Register gives a good overview of why this is the case; useful and interesting reading.
Solution 3 - Java
Just to give the reason behind what everyone else is saying.
The binary representation of a float is kind of annoying.
In binary, most programmers know the correlation between 1b=1d, 10b=2d, 100b=4d, 1000b=8d
Well it works the other way too.
.1b=.5d, .01b=.25d, .001b=.125, ...
The problem is that there is no exact way to represent most decimal numbers like .1, .2, .3, etc. All you can do is approximate in binary. The system does a little fudge-rounding when the numbers print so that it displays .1 instead of .10000000000001 or .999999999999 (which are probably just as close to the stored representation as .1 is)
Edit from comment: The reason this is a problem is our expectations. We fully expect 2/3 to be fudged at some point when we convert it to decimal, either .7 or .67 or .666667.. But we don't automatically expect .1 to be rounded in the same way as 2/3--and that's exactly what's happening.
By the way, if you are curious the number it stores internally is a pure binary representation using a binary "Scientific Notation". So if you told it to store the decimal number 10.75d, it would store 1010b for the 10, and .11b for the decimal. So it would store .101011 then it saves a few bits at the end to say: Move the decimal point four places right.
(Although technically it's no longer a decimal point, it's now a binary point, but that terminology wouldn't have made things more understandable for most people who would find this answer of any use.)
Solution 4 - Java
> What is wrong with using == to compare floating point values?
Because it's not true that 0.1 + 0.2 == 0.3
Solution 5 - Java
I think there is a lot of confusion around floats (and doubles), it is good to clear it up.
-
There is nothing inherently wrong in using floats as IDs in standard-compliant JVM [*]. If you simply set the float ID to x, do nothing with it (i.e. no arithmetics) and later test for y == x, you'll be fine. Also there is nothing wrong in using them as keys in a HashMap. What you cannot do is assume equalities like
x == (x - y) + y
, etc. This being said, people usually use integer types as IDs, and you can observe that most people here are put off by this code, so for practical reasons, it is better to adhere to conventions. Note that there are as many differentdouble
values as there are longvalues
, so you gain nothing by usingdouble
. Also, generating "next available ID" can be tricky with doubles and requires some knowledge of the floating-point arithmetic. Not worth the trouble. -
On the other hand, relying on numerical equality of the results of two mathematically equivalent computations is risky. This is because of the rounding errors and loss of precision when converting from decimal to binary representation. This has been discussed to death on SO.
[*] When I said "standard-compliant JVM" I wanted to exclude certain brain-damaged JVM implementations. See this.
Solution 6 - Java
As of today, the quick & easy way to do it is:
if (Float.compare(sectionID, currentSectionID) == 0) {...}
However, the docs do not clearly specify the value of the margin difference (an epsilon from @Victor 's answer) that is always present in calculations on floats, but it should be something reasonable as it is a part of the standard language library.
Yet if a higher or customized precision is needed, then
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
is another solution option.
Solution 7 - Java
Foating point values are not reliable, due to roundoff error.
As such they should probably not be used for as key values, such as sectionID. Use integers instead, or long
if int
doesn't contain enough possible values.
Solution 8 - Java
This is a problem not specific to java. Using == to compare two floats/doubles/any decimal type number can potentially cause problems because of the way they are stored. A single-precision float (as per IEEE standard 754) has 32 bits, distributed as follows:
1 bit - Sign (0 = positive, 1 = negative)
8 bits - Exponent (a special (bias-127) representation of the x in 2^x)
23 bits - Mantisa. The actuall number that is stored.
The mantisa is what causes the problem. It's kinda like scientific notation, only the number in base 2 (binary) looks like 1.110011 x 2^5 or something similar. But in binary, the first 1 is always a 1 (except for the representation of 0)
Therefore, to save a bit of memory space (pun intended), IEEE deccided that the 1 should be assumed. For example, a mantisa of 1011 really is 1.1011.
This can cause some issues with comparison, esspecially with 0 since 0 cannot possibly be represented exactly in a float. This is the main reason the == is discouraged, in addition to the floating point math issues described by other answers.
Java has a unique problem in that the language is universal across many different platforms, each of which could have it's own unique float format. That makes it even more important to avoid ==.
The proper way to compare two floats (not-language specific mind you) for equality is as follows:
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
where ACCEPTABLE_ERROR is #defined or some other constant equal to 0.000000001 or whatever precision is required, as Victor mentioned already.
Some languages have this functionality or this constant built in, but generally this is a good habit to be in.
Solution 9 - Java
In addition to previous answers, you should be aware that there are strange behaviours associated with -0.0f
and +0.0f
(they are ==
but not equals
) and Float.NaN
(it is equals
but not ==
) (hope I've got that right - argh, don't do it!).
Edit: Let's check!
import static java.lang.Float.NaN;
public class Fl {
public static void main(String[] args) {
System.err.println( -0.0f == 0.0f); // true
System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
System.err.println( NaN == NaN); // false
System.err.println(new Float( NaN).equals(new Float( NaN))); // true
}
}
Welcome to IEEE/754.
Solution 10 - Java
Here is a very long (but hopefully useful) discussion about this and many other floating point issues you may encounter: What Every Computer Scientist Should Know About Floating-Point Arithmetic
Solution 11 - Java
First of all, are they float or Float? If one of them is a Float, you should use the equals() method. Also, probably best to use the static Float.compare method.
Solution 12 - Java
You can use Float.floatToIntBits().
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
Solution 13 - Java
you may want it to be ==, but 123.4444444444443 != 123.4444444444442
Solution 14 - Java
If you *have to* use floats, strictfp keyword may be useful.
Solution 15 - Java
The following automatically uses the best precision:
/**
* Compare to floats for (almost) equality. Will check whether they are
* at most 5 ULP apart.
*/
public static boolean isFloatingEqual(float v1, float v2) {
if (v1 == v2)
return true;
float absoluteDifference = Math.abs(v1 - v2);
float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
return absoluteDifference < 5 * maxUlp;
}
Of course, you might choose more or less than 5 ULPs (‘unit in the last place’).
If you’re into the Apache Commons library, the Precision
class has compareTo()
and equals()
with both epsilon and ULP.
Solution 16 - Java
Two different calculations which produce equal real numbers do not necessarily produce equal floating point numbers. People who use == to compare the results of calculations usually end up being surprised by this, so the warning helps flag what might otherwise be a subtle and difficult to reproduce bug.
Solution 17 - Java
Are you dealing with outsourced code that would use floats for things named sectionID and currentSectionID? Just curious.
@Bill K: "The binary representation of a float is kind of annoying." How so? How would you do it better? There are certain numbers that cannot be represented in any base properly, because they never end. Pi is a good example. You can only approximate it. If you have a better solution, contact Intel.
Solution 18 - Java
As mentioned in other answers, doubles can have small deviations. And you could write your own method to compare them using an "acceptable" deviation. However ...
There is an apache class for comparing doubles: org.apache.commons.math3.util.Precision
It contains some interesting constants: SAFE_MIN
and EPSILON
, which are the maximum possible deviations of simple arithmetic operations.
It also provides the necessary methods to compare, equal or round doubles. (using ulps or absolute deviation)
Solution 19 - Java
In one line answer I can say, you should use:
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
To make you learned more about using related operators correctly, I am elaborating some cases here: Generally, there are three ways to test strings in Java. You can use ==, .equals (), or Objects.equals ().
How are they different? == tests for the reference quality in strings meaning finding out whether the two objects are the same. On the other hand, .equals () tests whether the two strings are of equal value logically. Finally, Objects.equals () tests for any nulls in the two strings then determine whether to call .equals ().
Ideal operator to use
Well this has been subject to lots of debates because each of the three operators have their unique set of strengths and weaknesses. Example, == is often a preferred option when comparing object reference, but there are cases where it may seem to compare string values as well.
However, what you get is a falls value because Java creates an illusion that you are comparing values but in the real sense you are not. Consider the two cases below:
#Case 1:
String a="Test";
String b="Test";
if(a==b) ===> true
#Case 2:
String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);
So, it’s way better to use each operator when testing the specific attribute it’s designed for. But in almost all cases, Objects.equals () is a more universal operator thus experience web developers opt for it.
Here you can get more details: <http://fluentthemes.com/use-compare-strings-java/>
Solution 20 - Java
The correct way would be
java.lang.Float.compare(float1, float2)
Solution 21 - Java
One way to reduce rounding error is to use double rather than float. This won't make the problem go away, but it does reduce the amount of error in your program and float is almost never the best choice. IMHO.