When I run:
System.out.println(1f - 0.9f);
I get:
0.100000024
This is because 0.1 has no representation in binary.
Then why when I print this:
System.out.println(0.1f);
I get this:
0.1
When I run:
System.out.println(1f - 0.9f);
I get:
0.100000024
This is because 0.1 has no representation in binary.
Then why when I print this:
System.out.println(0.1f);
I get this:
0.1
0.1f
Java’s float
uses IEEE-754 basic 32-bit binary floating-point. In binary
floating-point, every representable number is an integer multiple of some
power of two. (This includes non-negative powers 1, 2, 4, 8, 16,…, and it
includes negative powers ½, ¼, ⅛, 1/16, 1/32,…)
For numbers from 1 to 2, the representable numbers are multiples of 2−23, which is 0.00000011920928955078125:
1.00000000000000000000000 1.00000011920928955078125 1.00000023841857910156250 1.00000035762786865234375 1.00000047683715820312500 …
For numbers near 0.1, the representable numbers are multiples of 2−27, which is 0.000000007450580596923828125:
… 0.099999979138374328613281250 (a) 0.099999986588954925537109375 (b) 0.099999994039535522460937500 (c) 0.100000001490116119384765625 (d) 0.100000008940696716308593750 (e) 0.100000016391277313232421875 (f) …
(I labeled the numbers (a) to (f) to refer to them in text below.)
As we can see, the closest of these to 0.1 is (d), 0.100000001490116119384765625.
Thus, when 0.1
appears in source code, it is converted to this value,
0.100000001490116119384765625.
This is a general rule—any numeral in source code is converted to the nearest representable number.
(Note that 0.1 is not “represented by” 0.100000001490116119384765625, and
0.100000001490116119384765625 does not “represent” 0.1. The float
0.100000001490116119384765625 is exactly that. The 0.1f
in source text was
converted to 0.100000001490116119384765625 and is now just that value.)
0.1f
is not 0.1, why is “0.1” printed?Java’s default formatting for floating-point numbers uses the fewest significant decimal digits needed to distinguish the number from nearby representable numbers.
The rule for Java SE 10 can be found in the documentation for java.lang.float, in
the toString(float d)
section. I quote the passage below1. The
critical part says:
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
. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument f. Then f must be thefloat
value nearest to x; or, if twofloat
values are equally close to x, then f must be one of them and the least significant bit of the significand of f must be 0.
Let us see how this applies while formatting 0.100000001490116119384765625 and 0.099999994039535522460937500.
For 0.100000001490116119384765625, which is (d), we will first consider formatting one digit
after the decimal point: “0.1”. This represents the number 0.1, of course.
Now, we ask: Is this good enough? If we take 0.1 and ask which number in the
list of nearby numbers above is closest, what is the answer? The nearest number
in the list is (d), 0.100000001490116119384765625. That is the number we are
formatting, so we are done, and the result is “0.1”. This does not mean the float
is 0.1, just that, when it is converted to a string with default options, the result is the string “0.1”.
Now consider 0.099999994039535522460937500, which is (c). Again, if we consider using just one digit, the number rounds to 0.1. When we ask which number in the list is closest to that, the answer is (d), 0.100000001490116119384765625. That is not the number we are formatting, so we need more digits. If we consider two digits, rounding would give us 0.10, and that clearly is also not enough. Considering more and more digits gives us 0.100, 0.1000, and so on, until we get to eight digits. With eight digits, 0.099999994039535522460937500 rounds to 0.09999999. Now, when we check the list, we see the nearest number is (b), 0.099999986588954925537109375. (Adding about 0.0000000035 to that produces 0.09999999, whereas the number we are formatting is about 0.0000000040 away, which is farther.) So we try nine digits, which gives us 0.099999994. Finally, the closest number in the list is (c), 0.099999994039535522460937500, which is the number we are formatting, so we are done, and the result is “0.099999994”.
1 The documentation for toString(float d)
says:
Returns a string representation of the
float argument
. All characters mentioned below are ASCII characters.
If the argument is NaN, the result is the string "NaN".
Otherwise, the result is a string that represents the sign and magnitude (absolute value) of the argument. If the sign is negative, the first character of the result is '
-
' ('\u002D'
); if the sign is positive, no sign character appears in the result. As for the magnitude m:
If m is infinity, it is represented by the characters "Infinity"; thus, positive infinity produces the result "Infinity" and negative infinity produces the result "-Infinity".
If m is zero, it is represented by the characters "0.0"; thus, negative zero produces the result "-0.0" and positive zero produces the result "0.0".
If m is greater than or equal to 10-3 but less than 107, then it is represented as the integer part of m, in decimal form with no leading zeroes, followed by '
.
' ('\u002E'
), followed by one or more decimal digits representing the fractional part of m.If m is less than 10-3 or greater than or equal to 107, then it is represented in so-called "computerized scientific notation." Let n be the unique integer such that 10n ≤ m < 10n+1; then let a be the mathematically exact quotient of m and 10n so that 1 ≤ a < 10. The magnitude is then represented as the integer part of a, as a single decimal digit, followed by '
.
' ('\u002E'
), followed by decimal digits representing the fractional part of a, followed by the letter 'E
' ('\u0045'
), followed by a representation of n as a decimal integer, as produced by the methodInteger.toString(int)
.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
. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument f. Then f must be thefloat
value nearest to x; or, if twofloat
values are equally close to x, then f must be one of them and the least significant bit of the significand of f must be 0.
0.1 can be represented better in floating point than 0.9. Loosely speaking that's because 0.1 is smaller and closer to its nearest dyadic rational.
So the error when subtracting from 1.0 is larger.
Hence the two values differ.
The embedded formatting heuristics in println do a better job with the 0.1
System.out.println
, and most methods of converting floating-point numbers to strings, operate using the following rule: they use exactly as many digits are necessary so that the true value of the double
is the closest representable number to the printed value.
That is, it only prints out the digits 0.1
because the true value of the double
, 0.1000000000000000055511151231257827021181583404541015625
, is the closest double
to the displayed value.