views:

2562

answers:

18

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?

+31  A: 

Checking for equality with floats can be tricky because float math isn't very exact. You'll find a good article on the topic here: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

David Hedlund
+12  A: 

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.0" and then printing it out again, you may get a reported value of something like "5.99999999999". 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.

McWafflestix
since a float is usually stored scientific notation (whole/fraction), isn't 6.00 (0110b/0000b) always going to equal 6.00 (0110b/0000b)? Or to put it another way, when the fractional part has a value besides zero, the fraction retrieved may not always evaluate the same way (5.999999999 may be retrieved as 5.999999998)
KevinDTimm
@kevindtimm: Added a link to the answer to help provide more data as to why this is the case.
McWafflestix
"6.0" is a bad example, because it happens to be exactly representable by a float. "6.1" is a better example, as it is not exactly representable as a float and will instead be stored as 6.099999904632568359375 internally
Chris Dodd
@Chris Dodd - right, which is what I said.
KevinDTimm
@kevindtimm : so you will do your equality tests like so then if (number == 6.099999904632568359375) any time you wish to know of number is equals to 6.1... Yes you are correct... everything in the computer is strictly deterministic, just that the approximations used for floats are counter-intuitive when doing math problems.
Newtopian
+2  A: 

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.

omerkudat
+5  A: 

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.

FarmBoy
Agreed. Given that these are IDs, there is no reason to complicate things with floating point arithmetic.
Yohnny
Or a long. Depending on how many unique IDs get generated in the future, an int may not be large enough.
Wayne Hartman
+6  A: 

The correct way would be

java.lang.Float.compare(float1, float2)
Eric
Float.compare(float1, float2) returns an int, so it cannot be used instead of float1 == float2 in the if condition. Moreover, it doesn't really solve the underlying problem which this warning is referring to -- that if floats are results of numerical calculation, float1 != float2 may occur just due to rounding errors.
quant_dev
right, you cannot copy paste, you have to check the doc first.
Eric
What you can do instead of float1 == float2 is Float.compare(float1,float2) == 0.
deterb
This doesn't buy you anything - you still get `Float.compare(1.1 + 2.2, 3.3) != 0`
Pavel Minaev
+35  A: 

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.

Victor
+2  A: 

you may want it to be ==, but 123.4444444444443 != 123.4444444444442

KM
+3  A: 

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.

Tom Hawtin - tackline
If something is ==, then they are identical down to the bit. How could they not be equals()? Maybe you have it backwards?
Matt Kane
Added test code. == is screwy for float and doubles.
Tom Hawtin - tackline
@Matt NaN is special. Double.isNaN(double x) in Java is actually implemented as { return x != x; }...
quant_dev
With floats, `==` doesn't mean that numbers are "identical to the bit" (the same number can be represented with different bit patterns, though only one of them is normalized form). As well, `-0.0f` and `0.0f` are represented by different bit patterns (the sign bit is different), but compare as equal with `==` (but not with `equals`). Your assumption that `==` is bitwise comparison is, generally speaking, wrong.
Pavel Minaev
+2  A: 

Here is a very long (but hopefully useful) discussion about this and many other floating point issues you may encounter: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Adam Goode
+4  A: 

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)

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.)

Bill K
You're describing fixed-point math.
Matt Kane
@Matt K - um, not fixed point; if you "save a few bits at the end to say move the decimal point [N] bits to the right", that is floating point. Fixed point takes the position of the radix point to be, well, fixed. Also, in general, since shifting the binamal (?) point can always be made to leave you with a '1' in the leftmost position, you will find some systems that omit the leading '1', devoting the space thus liberated (1 bit!) to extending the range of the exponent.
JustJeff
The problem has nothing to do with binary vs. decimal representation. With decimal floating-point, you still have things like (1 / 3) * 3 == 0.9999999999999999999999999999.
dan04
@dan04 yes, because 1/3 has no decimal OR binary representation, it does have a trinary representation and would convert correctly that way :). The numbers I listed (.1, .25, etc) all have perfect decimal representations but no binary representation--and people are used to those having "exact" representations. BCD would handle them perfectly. That's the difference.
Bill K
+4  A: 

What is wrong with using == to compare floating point values?

Because it's not true that 0.1 + 0.2 == 0.3

AakashM
You can easily see this using this convertor applet: http://www.h-schmidt.net/FloatApplet/IEEE754.html
Joeri Sebrechts
+2  A: 

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.

VoiceOfUnreason
+1  A: 

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.

xcramps
+3  A: 

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.

CodeFusionMobile
Java has a defined behavior for floats. It is not platform dependent.
Yishai
+2  A: 

I think there is a lot of confusion around floats (and doubles), it is good to clear it up.

  1. 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 different double values as there are long values, so you gain nothing by using double. Also, generating "next available ID" can be tricky with doubles and requires some knowledge of the floating-point arithmetic. Not worth the trouble.

  2. 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.

quant_dev
+3  A: 

Mandatory link :-)

What Every Computer Scientist Should Know About Floating-Point Arithmetic

nos
A: 

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.

Peter Lawrey
+2  A: 

If you *have to* use floats, strictfp keyword may be useful.

http://en.wikipedia.org/wiki/strictfp

sdcvvc