-1

I know about roundoff error in programming languages!

System.out.println(0.1 + 0.1 + 0.1);

this code output is 0.30000000000000004 because 0.3 in binary needs an infinite number of digits to be represented and 0.3 is an irrational number in binary. but what about this one?

System.out.println(0.1 + 0.1);

why output is 0.2 ? 0.2 is also an irrational number in binary! so the output should be 0.200000002 or 0.1999999999! what's the difference between them?

  • [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) and [Is Floating Point Broken?](https://stackoverflow.com/q/588004/18157) – Jim Garrison Apr 02 '20 at 21:34

2 Answers2

0

When you convert a floating point number to a String in Java, it's done in a way that uses the least number of digits necessary to distinguish the number from the adjacent numbers.

This means that 0.2 is displayed as "0.2", since you don't need any more digits. The true value of 0.2 is of course a bit greater than 0.2:

jshell> new BigDecimal(0.2)
$1 ==> 0.200000000000000011102230246251565404236316680908203125

Another interpretation of your question is "why is 0.1 + 0.1 equal to 0.2?"

It's because the error in computing 0.1+0.1 is not large enough to make it become distinct from 0.2. It's of course not exactly the same value as 0.2, but out of all floating point numbers 0.2 is the closest.

jshell> new BigDecimal(0.1).add(new BigDecimal(0.1))
$2 ==> 0.2000000000000000111022302462515654042363166809082031250
Joni
  • 108,737
  • 14
  • 143
  • 193
  • so what about 0.3? according to your answer this approach should be apply to 0.3 but why the output is 0.30000000000000004? and not 0.3? – Sajjad.mp Apr 02 '20 at 21:25
  • The floating point number closest to 0.3 is slightly less than 0.3 (you can easily check with BigDecimal like in the answer). At the same time `0.1+0.1+0.1` is slightly more than 0.3 (remember that `0.1` is greater than 0.1). This means that `0.1+0.1+0.1` cannot possibly be `0.3` and in fact it must be a little bit more. – Joni Apr 02 '20 at 21:30
0

When a double value is printed, Java uses the return value of Double.toString(double), which says:

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.

So, lets print the values of 0.1 - 1.0, and their adjacent values. We use Math.nextDown(double) and Math.nextUp(double) to find the adjacent values. We use new BigDecimal(double) and toPlainString() to see more digits from the value.

We also calculate the value from summing multiple 0.1 values and mark that value with a <, =, or >, as appropriate.

System.out.printf("%9s%-13s%-12s%s%n", "", "double", "sum", "BigDecimal");
double sum = 0.1;
for (int i = 1; i <= 10; i++, sum += 0.1) {
    double val = i / 10d;
    double down = Math.nextDown(val);
    double up = Math.nextUp(val);
    System.out.printf("%d:%n  %-21s%-3s%s%n  %-21s%-3s%s%n  %-21s%-3s%s%n",
                      i,
                      down, (sum == down ? "<" : " "), new BigDecimal(down).toPlainString(),
                      val, (sum == val ? "=" : " "), new BigDecimal(val).toPlainString(),
                      up, (sum == up ? ">" : " "), new BigDecimal(up).toPlainString());
}

Output

         double       sum         BigDecimal
1:
  0.09999999999999999     0.09999999999999999167332731531132594682276248931884765625
  0.1                  =  0.1000000000000000055511151231257827021181583404541015625
  0.10000000000000002     0.10000000000000001942890293094023945741355419158935546875
2:
  0.19999999999999998     0.1999999999999999833466546306226518936455249786376953125
  0.2                  =  0.200000000000000011102230246251565404236316680908203125
  0.20000000000000004     0.2000000000000000388578058618804789148271083831787109375
3:
  0.29999999999999993     0.29999999999999993338661852249060757458209991455078125
  0.3                     0.299999999999999988897769753748434595763683319091796875
  0.30000000000000004  >  0.3000000000000000444089209850062616169452667236328125
4:
  0.39999999999999997     0.399999999999999966693309261245303787291049957275390625
  0.4                  =  0.40000000000000002220446049250313080847263336181640625
  0.4000000000000001      0.400000000000000077715611723760957829654216766357421875
5:
  0.49999999999999994     0.499999999999999944488848768742172978818416595458984375
  0.5                  =  0.5
  0.5000000000000001      0.50000000000000011102230246251565404236316680908203125
6:
  0.5999999999999999      0.5999999999999998667732370449812151491641998291015625
  0.6                  =  0.59999999999999997779553950749686919152736663818359375
  0.6000000000000001      0.600000000000000088817841970012523233890533447265625
7:
  0.6999999999999998      0.69999999999999984456877655247808434069156646728515625
  0.7                  =  0.6999999999999999555910790149937383830547332763671875
  0.7000000000000001      0.70000000000000006661338147750939242541790008544921875
8:
  0.7999999999999999   <  0.79999999999999993338661852249060757458209991455078125
  0.8                     0.8000000000000000444089209850062616169452667236328125
  0.8000000000000002      0.80000000000000015543122344752191565930843353271484375
9:
  0.8999999999999999   <  0.899999999999999911182158029987476766109466552734375
  0.9                     0.90000000000000002220446049250313080847263336181640625
  0.9000000000000001      0.9000000000000001332267629550187848508358001708984375
10:
  0.9999999999999999   <  0.99999999999999988897769753748434595763683319091796875
  1.0                     1
  1.0000000000000002      1.0000000000000002220446049250313080847263336181640625

As you can see, because of cumulative rounding issues, the summed value is not always exactly the value closest to what you'd expect, so it has to print extra digits for that "unique" value.

The summed value is wrong for 0.3, 0.8, 0.9, and 1.0.

Andreas
  • 154,647
  • 11
  • 152
  • 247