Reputation: 981
Code:
class Main {
public static void main (String[] args) {
System.out.print("float: ");
System.out.println(1.35f-0.00026f);
System.out.print("double: ");
System.out.println(1.35-0.00026);
}
}
Output:
float: 1.34974
double: 1.3497400000000002
??? float got the right answer, but double is adding extra stuff from no where, Why??
Isn't double supposed to be more precise than float?
Upvotes: 8
Views: 15284
Reputation: 1073968
Perversely, you see what you're seeing because double
is more precise than float
, rather than in spite of it. What you're seeing is a result of how floating point numbers are converted to strings by the default toString
operation of each class, which depends in part on the precision of the format of the floating point number.
TL;DR: The default "to string" operations for float
and double
each create strings that only contain as many digits as necessary to uniquely identify the value in the float
or double
relative to the adjacent representable values in that same format, rather than outputting all digits of the value. Since double
can represent more values than float
, its string has to be more precise.
Details:
Let's start here: The actual value you get from 1.35f - 0.00026f
isn't 1.34974
, it's 1.34974002838134765625
(since, as you probably know, not all floating point numbers can be represented in either the float
or the double
format). The double
equivalent 1.35 - 0.00026
gives you a more accurate value: 1.349740000000000161861635206150822341442108154296875
. (I used baseconvert.com's IEEE-754 converter for these, double-checking the 32-bit results against h-schmidt.net and by using the code below.) Let's see that by using BigDecimal
to format those values to strings rather than toString
(live copy):
import java.math.BigDecimal;
public class Example {
private static String format(float value) {
return new BigDecimal(value).toPlainString();
}
private static String format(double value) {
return new BigDecimal(value).toPlainString();
}
public static void main(String[] args) throws Exception {
float f = 1.35f - 0.00026f;
double d = 1.35 - 0.00026;
System.out.println("Float: " + format(f));
// "Float: 1.34974002838134765625"
System.out.println("Double: " + format(d));
// "Double: 1.349740000000000161861635206150822341442108154296875"
}
}
In fact, neither 1.35
nor 0.00026
is precisely held by either float
or double
either, so the imprecision kicks in even before the subtraction.
Okay, so why is float
's toString
giving you just "1.34974"
when double
's toString
gives you "1.3497400000000002"
? Because the toString
methods of both classes (float
, double
) produce the shortest (usually) output for a value that differentiates it from the representable values adjacent to it on either side.
With float
, because float
is quite imprecise, the short string "1.34974"
is enough to identify the value the float
actually contains (and not one of the values next to it). That is, even though the actual value is 1.34974002838134765625
, not all of those digits are needed to differentiate it from other representable float
values, all you need in the realm of float
is "1.34974"
. Float.parseFloat("1.34974")
gives you the float
value 1.34974002838134765625
again.
Not so for double
, though, because double
has more representable values than float
does, so the string "1.34974"
doesn't uniquely identify the double
value 1.349740000000000161861635206150822341442108154296875
. In fact, if you parse "1.34974"
as a double
, you get 1.3497399999999999398170302811195142567157745361328125
instead. So it has to include more digits to differentiate the double
value you're making into a string from other double
values — because double
is more precise than float
.
Upvotes: 2
Reputation: 2058
The reason of this is the mechanism of numbers with floating point calculation. Also when java tries to print a float value, it truncates trailing zeroes. And, you know, double type uses 8 bytes and float uses 4. So the double precision is bigger.
So, when java calculates your float value, it gets something like 1.3497400 and truncates it before output. And when java calculates your double value, it gets more digits and so you get such result.
Try to execute this simple example for better understanding:
public class Test {
public static void main( String[] args ) {
float sum = 0;
for (int i = 1; i <= 10; i++) {
sum += 0.1;
}
System.out.println( sum );
}
}
public class Test {
public static void main( String[] args ) {
double sum = 0;
for (int i = 1; i <= 10; i++) {
sum += 0.1;
}
System.out.println( sum );
}
}
Upvotes: 3
Reputation: 172378
A float is 4 bytes wide, whereas a double is 8 bytes wide.
Check What Every Computer Scientist Should Know About Floating-Point Arithmetic
Surely the double has more precision so it has slightly less rounding error.
Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation.
On a side note:-
I would suggest if you want the exact decimal values then use java.math.BigDecimal
Upvotes: 12
Reputation: 1804
If you know something about the rules for converting double values to strings, which are specified by the documentation for Double.toString [Java-API], you know that the program prints the shortest decimal fraction sufficient to distinguish the double value from its nearest neighbor, with at least one digit before and after the decimal point.
Check Joshua Bloch's Java Puzzlers: Traps, Pitfalls, and Corner Cases - Puzzle 2: Time for a Change. He explains this question in that chapter.
Upvotes: 2