views:

146

answers:

6

In Java the floating point arithmetic is not represented precisely. For example following snippet of code

            float a = 1.2; 
            float b= 3.0;
            float c = a * b; 
            if(c == 3.6){
              System.out.println("c is 3.6");
            } else {
                System.out.println("c is not 3.6");
            } 

actually prints "c is not 3.6".

I'm not interested in precision beyond 3 decimals (#.###). How can I deal with this problem to multiply floats and compare them reliably?

Thanks much

+4  A: 

If you are interested in fixed precision numbers, you should be using a fixed precision type like BigDecimal, not an inherently approximate (though high precision) type like float. There are numerous similar questions on Stack Overflow that go into this in more detail, across many languages.

David M
+5  A: 

it's a general rule that floating point number should never be compared like (a==b), but rather like (abs(a-b) < delta) where delta is a small number.

A floating point value having fixed number of digits in decimal form does not necessary have fixed number of digits in binary form.

bobah
+1  A: 

Hi,

I think it has nothing to do with Java, it happens on any IEEE 754 floating point number. It is because of the nature of floating point representation. Any languages that use the IEEE 754 format will encounter the same problem.

As suggested by David above, you should use the method abs of java.lang.Math class to get the absolute value (drop the positive/negative sign).

You can read this: http://en.wikipedia.org/wiki/IEEE_754_revision and also a good numerical methods text book will address the problem sufficiently.

public static void main(String[] args) {
    float a = 1.2f;
    float b = 3.0f;
    float c = a * b;
        final float PRECISION_LEVEL = 0.001f;
    if(Math.abs(c - 3.6f) < PRECISION_LEVEL) {
        System.out.println("c is 3.6");
    } else {
        System.out.println("c is not 3.6");
    }
}
Daniel Baktiar
A: 

To compare two floats, f1 and f2 within precision of #.### I believe you would need to do like this:

((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5))

f1 * 1000 lifts 3.14159265... to 3141.59265, + 0.5 results in 3142.09265 and the (int) chops off the decimals, 3142. That is, it includes 3 decimals and rounds the last digit properly.

aioobe
Comparing using an epsilon is better: consider what happens if `f1 == 3.1414999999999` and `f2 == 3.1415000000001`.
Mark Dickinson
Shit. I though I had it :-) sure. I agree with you. Comparing using an epsilon is much better. But does it accurately compare two floats of to its 3 first decimals?
aioobe
+2  A: 

This is a weakness of all floating point representations, and it happens because some numbers that appear to have a fixed number of decimals in the decimal system, actually have an infinite number of decimals in the binary system. And so what you think is 1.2 is actually something like 1.199999999997 because when representing it in binary it has to chop off the decimals after a certain number, and you lose some precision. Then multiplying it by 3 actually gives 3.5999999...

http://docs.python.org/py3k/tutorial/floatingpoint.html <- this might explain it better (even if it's for python, it's a common problem of the floating point representation)

Andrei Fierbinteanu
+1 - *all* finite precision floating number systems suffer from this problem. Not matter what base you choose, some rationals cannot be represented exactly.
Stephen C
+1  A: 

Like the others wrote:

Compare floats with: if (Math.abs(a - b) < delta)

You can write a nice method for doing this:

public static int compareFloats(float f1, float f2, float delta)
{
    if (Math.abs(f1 - f2) < delta)
    {
         return 0;
    } else
    {
        if (f1 < f2)
        {
            return -1;
        } else {
            return 1;
        }
    }
}

/**
 * Uses <code>0.001f</code> for delta.
 */
public static int compareFloats(float f1, float f2)
{
     return compareFloats(f1, f2, 0.001f);
}

So, you can use it like this:

if (compareFloats(a * b, 3.6f) == 0)
{
    System.out.println("They are equal");
}
else
{
    System.out.println("They aren't equal");
}
Martijn Courteaux