1
public static void main(String[] args) {

        double sum=860.10+1808.09;
        double sum1=1808.09+860.10;
        
        System.out.println(sum);// output is  2668.19
        System.out.println(sum1);// output is  2668.19

        List<Double> lst=new ArrayList<Double>();
        lst.add(860.10);
        lst.add(1808.09);
        Double res=lst.stream().mapToDouble(d->d).sum();
        System.out.println(res);// output is  2668.1900000000005
        
        lst.clear();
        //adding in reverse order
        lst.add(1808.09);
        lst.add(860.10);
        
        res=lst.stream().mapToDouble(d->d).sum();
        System.out.println(res);// output is  2668.19
    }

Why mapToDouble sum method is producing wrong output sometimes? Why the order is important here, both the inputs are of type double.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
Chenna
  • 39
  • 3
  • 1
    The difference arises because `860.10+1808.09` and `1808.09+860.10` are constant expressions: [prior to Java 17](https://docs.oracle.com/javase/specs/jls/se16/html/jls-15.html#jls-15.29), constant expressions are evaluated using strict floating point math, so they will have the same value in both cases. `DoubleStream.sum`, however, does not use strict fp math; so there is the possibility for discrepancy in the result. – Andy Turner Nov 02 '21 at 10:33
  • 1
    This question is **not** a duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – This question is not about *why* there are rounding errors when using floating-point values, but merely about *the difference* between adding those values directly, or within a stream. – MC Emperor Nov 02 '21 at 10:57
  • @AndyTurner – Does that not using StrictFP will sufficiently explain the different results when just the *sequence* of values is changing? – tquadrat Nov 02 '21 at 13:35
  • I actually can't explain why it's different, but I have been able to [repro on ideone](https://ideone.com/TocgmY). BTW, it's easier to use `DoubleStream.of(860.10, 1808.09).sum()` and `DoubleStream.of(1808.09, 860.10).sum()` than to create lists. – Andy Turner Nov 02 '21 at 14:50
  • 2
    @AndyTurner constant expressions are still strict fp in Java 17, in fact, *everything* is strict fp in Java 17. – Holger Nov 03 '21 at 15:19
  • 1
    @tquadrat no, the differences in the stream implementation are deliberate. Whether you specify `strictfp` or not, makes no difference for quite some time. That’s why Java 17 [declared](https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.1.1.3) [it](https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.4.3.5) [obsolete](https://docs.oracle.com/javase/specs/jls/se17/html/jls-9.html#jls-9.1.1.2). – Holger Nov 03 '21 at 16:03

1 Answers1

2

The Javadoc of Double.sum() says (emphasis mine):

If floating-point summation were exact, this method would be equivalent to:

 return reduce(0, Double::sum);

However, since floating-point summation is not exact, the above code is not necessarily equivalent to the summation computation done by this method. The value of a floating-point sum is a function both of the input values as well as the order of addition operations. The order of addition operations of this method is intentionally not defined to allow for implementation flexibility to improve the speed and accuracy of the computed result. 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. Because of the unspecified order of operations and the possibility of using differing summation schemes, the output of this method may vary on the same input elements.

You can dig into to why these are different, for example by looking at the source code of DoublePipeline.sum(). In particular, this makes use of a package-private collector, Collectors.sumWithCompensation.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 2
    It’s worth noting that neither, `860.10`, `1808.09`, nor the expected result of `2668.19` is exactly representable as `double` value, so all three values are subject to rounding to begin with. – Holger Nov 03 '21 at 16:17