0

like a decimal number 0.1, represented as binary 0.00011001100110011...., this is a infinite repeating number. when I write code like this:

float f = 0.1f;

the program will rounding it as binary 0 01111011 1001 1001 1001 1001 1001 101, this is not original number 0.1. but when print this variable like this:

System.out.print(f);

I can get original number 0.1 rather than 0.100000001 or some other number. I think the program can't exactly represent "0.1", but it can display "0.1" exactly. How to do it?

I recover decimal number through add each bits of binary, it looks weird.

float f = (float) (Math.pow(2, -4) + Math.pow(2, -5) + Math.pow(2, -8) + Math.pow(2, -9) + Math.pow(2, -12) + Math.pow(2, -13) + Math.pow(2, -16) + Math.pow(2, -17) + Math.pow(2, -20) + Math.pow(2, -21) + Math.pow(2, -24) + Math.pow(2, -25));

float f2 = (float) Math.pow(2, -27);

System.out.println(f);

System.out.println(f2);

System.out.println(f + f2);

Output:

0.099999994

7.4505806E-9

0.1

in math, f1 + f2 = 0.100000001145... , not equals 0.1. Why the program would not get result like 0.100000001, I think it is more accurate.

AL-zami
  • 8,902
  • 15
  • 71
  • 130
  • 3
    There's actually an interesting question buried in here somewhere: Given that `0.1` isn't exactly `0.1`, what is deciding that `String.valueOf(0.1)` is `"0.1"`? – T.J. Crowder Feb 08 '15 at 14:47

2 Answers2

2

Java's System.out.print prints just enough decimals that the resulting representation, if parsed as a double or float, converts to the original double or float value.

This is a good idea because it means that in a sense, no information is lost in this kind of conversion to decimal. On the other hand, it can give an impression of exactness which, as you make clear in your question, is wrong.

In other languages, you can print the exact decimal representation of the float or double being considered:

#include <stdio.h>

int main(){
  printf("%.60f", 0.1);
}

result: 0.100000000000000005551115123125782702118158340454101562500000

In Java, in order to emulate the above behavior, you need to convert the float or double to BigDecimal (this conversion is exact) and then print the BigDecimal with enough digits. Java's attitude to floating-point-to-string-representing-a-decimal conversion is pervasive, so that even System.out.format is affected. The linked Java program, the important line of which is System.out.format("%.60f\n", 0.1);, shows 0.100000000000000000000000000000000000000000000000000000000000, although the value of 0.1d is not 0.10000000000000000000…, and a Java programmer could have been excused for expecting the same output as the C program.

To convert a double to a string that represents the exact value of the double, consider the hexadecimal format, that Java supports for literals and for printing.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Citations? Details? And note that it's almost certainly *not* `System.out.println`; `String.valueOf` does this (as well?). – T.J. Crowder Feb 08 '15 at 14:50
  • @T.J.Crowder When I say that `System.out.println` does this, I certainly do not mean that it is the only thing that does. The references for the claim are forthcoming. I don't know what details are missing from “ Java's System.out.print prints just enough decimals that the resulting representation, if parsed as a double or float, converts to the original double or float value.”. Seems quite detailed to me. – Pascal Cuoq Feb 08 '15 at 14:52
  • how the program know how many decimal is enough? I tried the code below, it is very interesting. float f1 = 0.1f; System.out.println(Integer.toHexString(Float.floatToRawIntBits(f1))); float f2 = 0.100000001f; System.out.println(Integer.toHexString(Float.floatToRawIntBits(f2))); The output is same, and f1 == f2. – user4543154 Feb 08 '15 at 15:08
  • @user4543154 The Java implementation knows how many digits to print because it uses this sort of algorithm, designed to stop when enough digits are printed: http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/ – Pascal Cuoq Feb 08 '15 at 15:12
1

I believe this is covered by Double.toString(double) (and similarly in Float#toString(float)):

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 double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

(my emphasis)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Since you are interested in details, Java's strange choices for the conversion from floating-point to decimal also affect `System.out.format`, which certainly does not invoke `Double.toString(double)`. – Pascal Cuoq Feb 08 '15 at 15:05