5

As explained in many places (such as Why can't decimal numbers be represented exactly in binary?), not all decimal fractions can be represented as floating point values (such as stored in a float in Java).

The typical example given is "0.2". According to this nifty IEEE 754 Converter, the float closest to 0.2 is approximately 0.20000000298023224 , so parsing "0.2" as a float should yield this result.

However, I noted that Java seems to be able to do the impossible:

String number="0.2";
float f = Float.parseFloat(number);
System.out.println("Result of roundtrip String -> float -> String: "+f);

prints:

Result of roundtrip String -> float -> String: 0.2

Test on IDeone.com

How does Java know that I wanted the (rounded) output "0.2", instead of the precise output "0.20000000298023224" as explained above?

The Javadocs of Float.toString() try to explain this:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type float.

Unfortunately, this confuses me even more. Why does "printing as many digits as needed to uniquely distinguish the argument value" allow Java to print "0.2"?

Ideally, the answer would also explain why this printing algorithm was chosen. It is to make (some) round-trips work, as in this example? Is there another motivation?

Community
  • 1
  • 1
sleske
  • 81,358
  • 34
  • 189
  • 227
  • 1
    Where is Jon Skeet when you need him? Did he BREXIT? – GhostCat Jun 24 '16 at 12:51
  • You know that even entering 0.21 or 0.2000001 or anything similar also returns 0.2? See http://stackoverflow.com/questions/17464675/convert-string-to-decimal-number-with-2-decimal-places-in-java –  Jun 24 '16 at 12:53
  • There are 7 zeroes, 10^7 = 2^23 so probably the 3 byte mantissa of a 4 byte float is reached. So here it might still be regular. It is more often C and C++ that cheat a bit to have nicer results (my impression). – Joop Eggen Jun 24 '16 at 12:54
  • 1
    Java assumes you want the shortest string that round-trips, which is generally a good assumption. But beware -- it doesn't always return the shortest string ( http://www.exploringbinary.com/java-doesnt-print-the-shortest-strings-that-round-trip/ ) – Rick Regan Jun 24 '16 at 22:53

2 Answers2

2

The rounding of the output is not because of some printing algorithm. It is because of the size of float. If you do:

String fs = "0.2";
double f = Float.parseFloat(fs);
System.out.println(f);

You get:

0.20000000298023224

Test on Ideone.com

This clearly shows that the String "0.2" is parsed as 0.20000000298023224, but the float data-type is not able to hold it, and rounds it up to 0.2. double data type is capable of holding this data, and hence, when you parse it as float, but store it in a double, you get the expected output.

dryairship
  • 6,022
  • 4
  • 28
  • 54
1

I guess 0.2 belongs to the "Value Set Conversion".

mauretto
  • 3,183
  • 3
  • 27
  • 28