6

I am confused. If I calculate

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

Then I get a result of 0.9999999999999999. But if I calculate

Double sum = DoubleStream.builder().add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).add(0.1).build().sum();
System.out.println(sum);

Then I get a result of 1.0. Why is there a difference?

Paolo Forgia
  • 6,572
  • 8
  • 46
  • 58
J. Doe9891
  • 129
  • 4
  • 2
    Related: https://stackoverflow.com/questions/588004/is-floating-point-math-broken – GhostCat Sep 11 '17 at 11:34
  • 3
    And keep in mind: the first line is **completely** computed by the compiler. It does constant folding and sums up these values for you. Check your byte code - you will not find an addition in there for option 1. – GhostCat Sep 11 '17 at 11:35
  • 2
    @GhostCat It's still a good question. Yes, floating point math is inexact, but it ought to be consistent. It's not random. What explains the difference? – John Kugelman Sep 11 '17 at 11:38
  • 3
    @GhostCat I disagree; the question is perfectly legitimate – fge Sep 11 '17 at 11:38
  • What's the best way to sum multiple values? – J. Doe9891 Sep 11 '17 at 11:47
  • @JohnKugelman Did I say it is a bad question? Did I close vote for *dup*? Neither of that. Actually I just put up two comments. – GhostCat Sep 11 '17 at 11:48
  • @GhostCat I fail to see the relationship – fge Sep 11 '17 at 11:54
  • @fge Thing is that the title directly got somebody to close-vote as "asking to recommend". And then the question doesn't show if the OP did do any research around the floating-point topic. – GhostCat Sep 11 '17 at 11:59

2 Answers2

10

The Javadoc of double java.util.stream.DoubleStream.sum() answers your question:

In particular, this method may be implemented using compensated summation or other technique to reduce the error bound in the numerical sum compared to a simple summation of double values.

In other words, the implementation of sum() doesn't have to use simple summation of double values (which can have accuracy issues, as you noticed in your first snippet), and therefore may return a more accurate result.

EDIT: Note that even though using DoubleStream's sum() appears to give a more accurate result, this is an implementation detail, so it's not guaranteed by the Javadoc. Besides, simple double addition is more efficient, since it doesn't have the overhead of constructing a DoubleStream. You have to decide if you prefer potential better accuracy or performance.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • I think it would be useful here to "bold" the citation; this is the crucial point – fge Sep 11 '17 at 11:43
  • 1
    @fge I think the entire citation (or most of it) is relevant, which is why I didn't put any of it in bold. It is a short enough quote anyway. – Eran Sep 11 '17 at 11:46
  • Yes, the whole citation is relevant, but don't you think, when reading the answer "at a first glance", that the whole citation would be missed? You know how the human mind works wrt visual clues and attention span, right? :) Especially since the reflex of clicking on the javadoc link and reading _the whole javadoc_ won't be as prevalent. Well, just my opinion ;) – fge Sep 11 '17 at 11:49
2

Just to augment Eran's answer, internally DoubleStream#sum uses Kahan Summation.

Relevant parts:

/**
 * Incorporate a new double value using Kahan summation /
 * compensation summation.
 *
 * High-order bits of the sum are in intermediateSum[0], low-order
 * bits of the sum are in intermediateSum[1], any additional
 * elements are application-specific.
 *
 */
static double[] sumWithCompensation(double[] intermediateSum, double value) { ....
Eugene
  • 117,005
  • 15
  • 201
  • 306