Comparing float and double primitives in Java
Take a look at What every computer scientist should know about floating point numbers.
Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation....
--- Edit to show what the above quote means ---
You shouldn't ever compare floats or doubles for equality; because, you can't really guarantee that the number you assign to the float or double is exact.
So
float x = 3.2f;
doesn't result in a float with a value of 3.2. It results in a float with a value of 3.2 plus or minus some very small error. Say 3.19999999997f. Now it should be obvious why the comparison won't work.
To compare floats for equality sanely, you need to check if the value is "close enough" to the same value, like so
float error = 0.000001 * second;
if ((first >= second - error) || (first <= second + error)) {
// close enough that we'll consider the two equal
...
}
The difference is that 3.5 can be represented exactly in both float
and double
- whereas 3.2 can't be represented exactly in either type... and the two closest approximations are different.
Imagine we had two fixed-precision decimal types, one of which stored 4 significant digits and one of which stored 8 significant digits, and we asked each of them to store the number closest to "a third" (however we might do that). Then one would have the value 0.3333 and one would have the value 0.33333333.
An equality comparison between float
and double
first converts the float
to a double
and then compares the two - which would be equivalent to converting 0.3333 in our "small decimal" type to 0.33330000. It would then compare 0.33330000 and 0.33333333 for equality, and give a result of false.
The common implementation of floating point numbers, IEEE754, allows for the precise representation of only those numbers which have a short, finite binary expansion, i.e. which are a sum of finitely many (nearby) powers of two. All other numbers cannot be precisely represented.
Since float
and double
have different sizes, the representation in both types for a non-representable value are different, and thus they compare as unequal.
(The length of the binary string is the size of the mantissa, so that's 24 for float
, 53 for double
and 64 for the 80-bit extended-precision float (not in Java). The scale is determined by the exponent.)
floating point is a binary format and it can represent numbers as a sum of powers of 2. e.g. 3.5 is 2 + 1 + 1/2.
float 3.2f as an approximation of 3.2 is
2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/4194304 + a small error
However double 3.2d as an approximation of 3.2 is
2 + 1 + 1/8+ 1/16+ 1/128+ 1/256+ 1/2048+ 1/4096+ 1/32768+ 1/65536+ 1/524288+ 1/1048576+ 1/8388608+ 1/16777216+ 1/134217728+ 1/268435456+ 1/2147483648+ 1/4294967296+ 1/34359738368+ 1/68719476736+ 1/549755813888+ 1/1099511627776+ 1/8796093022208+ 1/17592186044416+ 1/140737488355328+ 1/281474976710656+ 1/1125899906842624 + a smaller error
When you use floating point, you need to use appropriate rounding. If you use BigDecimal instead (and many people do) it has rounding built in.
double dd = 3.2;
float ff = 3.2f;
// compare the difference with the accuracy of float.
System.out.println(Math.abs(dd - ff) < 1e-7 * Math.abs(ff));
BTW the code I used to print the fractions for double.
double f = 3.2d;
double f2 = f - 3;
System.out.print("2+ 1");
for (long i = 2; i < 1L << 54; i <<= 1) {
f2 *= 2;
if (f2 >= 1) {
System.out.print("+ 1/" + i);
f2 -= 1;
}
}
System.out.println();