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
-
12What Every Computer Scientist Should Know About Floating-Point ArithmeticSotirios Delimanolis– Sotirios Delimanolis2013年09月13日 19:06:20 +00:00Commented Sep 13, 2013 at 19:06
-
1stackoverflow.com/questions/4915462/…kiheru– kiheru2013年09月13日 19:07:27 +00:00Commented Sep 13, 2013 at 19:07
-
1double double double double double double double.vikingsteve– vikingsteve2013年09月13日 19:08:14 +00:00Commented Sep 13, 2013 at 19:08
-
2Because 6.5 can be expressed exactly with binary floats, unlike 3.2.kiheru– kiheru2013年09月13日 19:11:32 +00:00Commented Sep 13, 2013 at 19:11
-
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– ajb2013年09月13日 20:14:39 +00:00Commented Sep 13, 2013 at 20:14
6 Answers 6
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.
4 Comments
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.
Comments
There are many ways to get around this issue,
This issue occurs because decimal values cannot be represented accurately in the binary.
- Have a tolerance value and check if the difference is less than the tolerance value.
- Multiply it by 10/100/... and then compare the numbers
- Look into BigDecimal.
And go through this for sure.
1 Comment
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.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.
Comments
:) 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.
4 Comments
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.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.