2

I was writing a sample program with floats but suddenly something weird happened. I would really appreciate if someone can shed some light on why I am facing such behavior from my program.

package Programs;

public class FloatTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        float f1 = (float) 3.2;
        float f2 = (float) 6.5;
        if (f1 == 3.2) {
            System.out.println(f1 + " same");
        } else {
            System.out.println(f1 + " different");
        }
        if (f2 == 6.5) {
            System.out.println(f2 + " same");
        } else {
            System.out.println(f2 + " different");
        }
    }
}

Output:

3.2 different
6.5 same

After doing some tests with changing values of f2 I noticed that I get unexpected result for f2 > 3.5 Why is that? Any input is really appreciated.

Thanks

Prateek
  • 1,916
  • 1
  • 12
  • 22
  • 12
    [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – Sotirios Delimanolis Sep 13 '13 at 19:06
  • 1
    http://stackoverflow.com/questions/4915462/how-should-i-do-floating-point-comparison – kiheru Sep 13 '13 at 19:07
  • 1
    double double double double double double double. – vikingsteve Sep 13 '13 at 19:08
  • Instead of (float) 3.2 you may write 3.2f and therefore in comparison you will compare with floats as well – Timofey Sep 13 '13 at 19:08
  • 1
    You're comparing floats to doubles. – toniedzwiedz Sep 13 '13 at 19:08
  • But how does it change in case of 6.5? It is still a double – Prateek Sep 13 '13 at 19:09
  • 2
    Because 6.5 can be expressed exactly with binary floats, unlike 3.2. – kiheru Sep 13 '13 at 19:11
  • possible duplicate of [Comparing float and double primitives in Java](http://stackoverflow.com/questions/7392167/comparing-float-and-double-primitives-in-java) – rolfl Sep 13 '13 at 19:15
  • The default for decimals is 64-bit double literal. So 3.2 is 3.2d the d is omitted by convention (but allowed). – ggb667 Sep 13 '13 at 19:20
  • Question: Is this True or False: `((float)3.2 == 3.2f)` ? – Lee Meador Sep 13 '13 at 19:27
  • @LeeMeador That expression evaluates to true, since by stating 3.2f and casting 3.2 to a float you are comparing identical values. – Surveon Sep 13 '13 at 19:32
  • @Surveon Does that imply that it is a rule that a repeating decimal fraction is evaluated to more bits than needed and rounded when creating float literals and a double is rounded when converted to a float? That's the only way I can think of that makes that 'true.' – Lee Meador Sep 13 '13 at 19:42
  • I am mainly asserting that it's true because I tried it out and it evaluated to true. As far as the value in memory is concerned, I would imagine the conversion from a double to a float simply involves discarding excess bits - the expressions should then be the same (before rounding). – Surveon Sep 13 '13 at 19:44
  • 2
    @LeeMeador The rule in Java is that when converting to a float or double, a number is rounded to the nearest value that can be represented, and to a value whose least significant bit is 0 if equidistant. This applies both to literals and to casting, so that for 3.2f, the float value will be the value closest to 3.2; and for (float)3.2, the literal 3.2 represents the *double* closest to 3.2, and then casting to float causes the result to be the float value closest to that double. These two should normally produce the same float, but there should be some rare corner cases that don't. – ajb Sep 13 '13 at 20:14

6 Answers6

5

I'm going to try my hand at a technical explanation for this. Since these values are being ultimately stored in the binary format, under some conditions precision will be lost.

6.5 should not lose any precision, as it can be converted to the binary value 110.1.

3.2, however, cannot be converted cleanly like this because the binary representation of .2 becomes irrational. It would be something along the lines of 11.00110011... This can only be, at best, rounded to 3.2 when converted the other way.

If somebody could verify what I'm saying, it would be fantastic - this is is based on an admittedly limited knowledge of how Java is handling this.

Surveon
  • 729
  • 5
  • 12
  • you have the gist of it here, so +1. – Bathsheba Sep 13 '13 at 19:16
  • Cheers - from what I understand, the only way to store a precise value like 3.2 is by using a class along the lines of BigDecimal as was mentioned in another answer. – Surveon Sep 13 '13 at 19:23
  • Indeed, although in most scientific number crunching, double precision is adequate. But you must never use floating point to represent money. – Bathsheba Sep 13 '13 at 19:24
  • @Bathsheba You can use float to represent money. It will drive you to drink if you need to do things like calculate interest or evaluate inventory when there are items that have values with parts less than 1 cent (in USD) [e.g. 12 for $1.00 = $.085 each] To make it work you and everyone that touches the code must be very careful to round the numbers at all the right points and add, subtract and even more multiply and divide in the right order. – Lee Meador Sep 13 '13 at 19:40
3

Due to the way that floating point variables are represented, not all numbers can be represented precisely. In fact, very few can.

When you write

float f1 = (float) 3.2;

and compare that with 3.2, you are comparing f1 (a float) with 3.2 (a double: 3.2 entered as a literal is implicitly a double type). In your statement f1 == 3.2, f1 gets implicitly converted to a double, but by then, precision has been lost. This is because 3.2 is one of those numbers that cannot be represented precisely and double makes a better job of it than float.

Coincidentally, 6.5 is one of those numbers (double or float) that can be expressed precisely due to the internal scheme that Java uses to represent floating point. That's why, in your case, f2 == 6.5 is true.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

There are many ways to get around this issue,

This issue occurs because decimal values cannot be represented accurately in the binary.

  1. Have a tolerance value and check if the difference is less than the tolerance value.
  2. Multiply it by 10/100/... and then compare the numbers
  3. Look into BigDecimal.

And go through this for sure.

JNL
  • 4,683
  • 18
  • 29
  • For tolerance, use something like this: `if (Math.abs(3.2f - f1) > 0.05f) ...`. Of course, we can argue all day long about the value to compare it to. 0.05 or 0.000001 or whatever. For your code, you will likely know how close is close enough. – Lee Meador Sep 13 '13 at 19:14
2

:) Ahh IEEE754, and JVM differences with floats. In brief 6.5 is not the same as the float value of 6.5. 6.5 == 6.5f will work, but you better understand what you are doing! Please read http://en.wikipedia.org/wiki/IEEE_floating_point, also note the 'strictfp' keyword for force IEEE754 behavior across platforms. There are many things to consider here, rounding behavior, order of precidence, JVM differences, etc. Things that arent integers or longs are unexpectedly tricky.

You are manipulating a binary representation of a number which has a precision that is appropariate for many types of mathematical calculations where a infinately precise answer is not required. For many Engineering and Financial systems, especially for those involving multiplication or fractions this is NOT ACCEPTABLE. You either need to rebase your accounting (using a financial class that understands money and decimals), (for instance counting in pennies for money), and for engineering you may need to use BigDecimal or a related class with specific rounding behavior, percision, etc.

To give another example the float value of 1/3 + 1/3 + 1/3, may or may not be equal to 1. Because the 1's and 0's that make up the digitial represenatation of the data are not precisely 1/3. On my particular platform (JVM 1.6 Windows 64 bit), it's 1.0, but it might not be on yours.

ggb667
  • 1,881
  • 2
  • 20
  • 44
  • I'm pretty sure that 1/3 + 1/3 + 1/3 is equal to 0. – pamphlet Sep 13 '13 at 19:19
  • @pamphlet Can you give the explanation to your statement? [May be this link could help to enlighten your math knowledge.](http://www.mathsisfun.com/fractions_addition.html) – Smit Sep 13 '13 at 19:24
  • @Smit, `1/3` in Java is integer arithmetic, and evaluates to `0`. I'm not sure if the link enlightened me, but it did make me hungry for pizza. – pamphlet Sep 13 '13 at 19:53
  • @pamphlet LOL in Java integer arithmetic sense, you are correct and sorry for link making you hungry. – Smit Sep 13 '13 at 19:59
2

use a cast if (f1 == (float)3.2) { then it will work.

literals like 3.2 are of type double and you are comparing a float with a double, and it causes such things to happen.

As @JNL pointed out

This issue occurs because decimal values cannot be represented accurately in the binary.

Thirumalai Parthasarathi
  • 4,541
  • 1
  • 25
  • 43
1

Although it may not seem correct, when you run

float f1 = (float) 3.2;

f1 is not really equal to 3.2. As mentioned, there are several ways to work around this issue.

Jim
  • 1,056
  • 1
  • 13
  • 19