1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.util.*;
  25 import java.util.stream.*;
  26 
  27 /*
  28  * @test
  29  * @bug 8006572
  30  */
  31 public class TestDoubleSummaryStatisticsSummation {
  32     public static void main(String... args) {
  33         double base = 1.0;
  34         double increment = Math.ulp(base)/2.0;
  35         int count = 1_000_001;
  36         /*
  37          * The exact sum of this stream is 1 + 1e6*ulp(1.0) but a
  38          * naive summation algorithm will return 1.0 since (1.0 +
  39          * ulp(1.0)/2) will round to 1.0 again.
  40          */
  41         TestDoubleIterator tdi = new TestDoubleIterator(base,
  42                                                         increment,
  43                                                         count);
  44         DoubleSummaryStatistics stats =
  45             StreamSupport.doubleStream(Spliterators.spliteratorUnknownSize(tdi,
  46                                                                            Spliterator.IMMUTABLE |
  47                                                                            Spliterator.NONNULL),
  48                                        false).collect(DoubleSummaryStatistics::new,
  49                                                       DoubleSummaryStatistics::accept,
  50                                                       DoubleSummaryStatistics::combine);
  51 
  52         double expectedSum = base + (increment * (count - 1));
  53         compareUlpDifference(expectedSum, stats.getSum(), 3);
  54 
  55         double expectedAvg = expectedSum / count;
  56         compareUlpDifference(expectedAvg, stats.getAverage(), 3);
  57     }
  58 
  59     private static void compareUlpDifference(double expected, double computed, double threshold) {
  60         double ulpDifference = Math.abs(expected - computed) / Math.ulp(expected);
  61 
  62         if (ulpDifference > threshold) {
  63             System.out.printf("Numerical summation error too large, %g ulps rather than %g.%n",
  64                               ulpDifference, threshold);
  65             throw new RuntimeException();
  66         }
  67 
  68     }
  69 
  70     static class TestDoubleIterator implements PrimitiveIterator.OfDouble {
  71         private double base;
  72         private double subsequent;
  73         private long count;
  74         private long iteration = 0;
  75 
  76         /**
  77          * Construct a {@code TestDoubleStream} of {@code count}
  78          * elements whose first value is {@code base} and whose
  79          * following values are all {@code subsequent}.
  80          */
  81         TestDoubleIterator(double base, double subsequent, long count) {
  82             this.base = base;
  83             this.subsequent = subsequent;
  84             this.count = count;
  85         }
  86 
  87         @Override
  88         public boolean hasNext() {
  89             return (iteration < count);
  90         }
  91 
  92         @Override
  93         public double nextDouble() {
  94             if (iteration >= count)
  95                 throw new NoSuchElementException();
  96             else {
  97                 iteration++;
  98                 return (iteration == 1) ? base : subsequent;
  99             }
 100         }
 101     }
 102 }