From d7fc39484d0a39d168a612f0a57bff9dd9626054 Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Mon, 12 Apr 2021 14:25:02 -0500 Subject: [PATCH 1/6] 8214761: Bug in parallel Kahan summation implementation --- .../classes/java/util/DoubleSummaryStatistics.java | 4 +++- .../share/classes/java/util/stream/Collectors.java | 13 ++++++++++--- .../classes/java/util/stream/DoublePipeline.java | 8 +++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java index 91628353c623b..b4b1dfd245f88 100644 --- a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java @@ -156,7 +156,9 @@ public void combine(DoubleSummaryStatistics other) { count += other.count; simpleSum += other.simpleSum; sumWithCompensation(other.sum); - sumWithCompensation(other.sumCompensation); + + //Negating this value because low-order bits are in negated form + sumWithCompensation(-other.sumCompensation); min = Math.min(min, other.min); max = Math.max(max, other.max); } diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index 0cf8cdbc7f702..14a66845459e1 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -734,7 +734,8 @@ public static Collector collectingAndThen(Collector dow a[2] += val;}, (a, b) -> { sumWithCompensation(a, b[0]); a[2] += b[2]; - return sumWithCompensation(a, b[1]); }, + //Negating this value because low-order bits are in negated form + return sumWithCompensation(a, -b[1]); }, a -> computeFinalSum(a), CH_NOID); } @@ -840,13 +841,19 @@ static double computeFinalSum(double[] summands) { /* * In the arrays allocated for the collect operation, index 0 * holds the high-order bits of the running sum, index 1 holds - * the low-order bits of the sum computed via compensated + * the negated low-order bits of the sum computed via compensated * summation, and index 2 holds the number of values seen. */ return new CollectorImpl<>( () -> new double[4], (a, t) -> { double val = mapper.applyAsDouble(t); sumWithCompensation(a, val); a[2]++; a[3]+= val;}, - (a, b) -> { sumWithCompensation(a, b[0]); sumWithCompensation(a, b[1]); a[2] += b[2]; a[3] += b[3]; return a; }, + (a, b) -> { + sumWithCompensation(a, b[0]); + //Negating this value because low-order bits are in negated form + sumWithCompensation(a, -b[1]); + a[2] += b[2]; a[3] += b[3]; + return a; + }, a -> (a[2] == 0) ? 0.0d : (computeFinalSum(a) / a[2]), CH_NOID); } diff --git a/src/java.base/share/classes/java/util/stream/DoublePipeline.java b/src/java.base/share/classes/java/util/stream/DoublePipeline.java index 66d4ed719ee41..9315c00c6d520 100644 --- a/src/java.base/share/classes/java/util/stream/DoublePipeline.java +++ b/src/java.base/share/classes/java/util/stream/DoublePipeline.java @@ -442,7 +442,7 @@ public final double sum() { /* * In the arrays allocated for the collect operation, index 0 * holds the high-order bits of the running sum, index 1 holds - * the low-order bits of the sum computed via compensated + * the negated low-order bits of the sum computed via compensated * summation, and index 2 holds the simple sum used to compute * the proper result if the stream contains infinite values of * the same sign. @@ -454,7 +454,8 @@ public final double sum() { }, (ll, rr) -> { Collectors.sumWithCompensation(ll, rr[0]); - Collectors.sumWithCompensation(ll, rr[1]); + //Negating this value because low-order bits are in negated form + Collectors.sumWithCompensation(ll, -rr[1]); ll[2] += rr[2]; }); @@ -497,7 +498,8 @@ public final OptionalDouble average() { }, (ll, rr) -> { Collectors.sumWithCompensation(ll, rr[0]); - Collectors.sumWithCompensation(ll, rr[1]); + //Negating this value because low-order bits are in negated form + Collectors.sumWithCompensation(ll, -rr[1]); ll[2] += rr[2]; ll[3] += rr[3]; }); From a35e6db46fbe02acbeaad1fc64f2147ada43a901 Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Mon, 12 Jul 2021 15:43:17 -0500 Subject: [PATCH 2/6] Stashing --- .../java/util/DoubleSummaryStatistics.java | 2 +- .../classes/java/util/stream/Collectors.java | 2 +- .../DoubleStreamSums/CompensatedSums.java | 118 ++++++++++++++++++ .../NegativeCompensation.java | 72 +++++++++++ 4 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 test/jdk/java/util/DoubleStreamSums/CompensatedSums.java create mode 100644 test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java diff --git a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java index b4b1dfd245f88..afec33a6114d0 100644 --- a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java @@ -243,7 +243,7 @@ public final long getCount() { */ public final double getSum() { // Better error bounds to add both terms as the final sum - double tmp = sum + sumCompensation; + double tmp = sum - sumCompensation; if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) // If the compensated sum is spuriously NaN from // accumulating one or more same-signed infinite values, diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index 14a66845459e1..c1a552e6cfe9c 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -767,7 +767,7 @@ static double[] sumWithCompensation(double[] intermediateSum, double value) { */ static double computeFinalSum(double[] summands) { // Better error bounds to add both terms as the final sum - double tmp = summands[0] + summands[1]; + double tmp = summands[0] - summands[1]; double simpleSum = summands[summands.length - 1]; if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) return simpleSum; diff --git a/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java new file mode 100644 index 0000000000000..5abb569836304 --- /dev/null +++ b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8214761 + */ + +import java.util.Random; +import java.util.stream.DoubleStream; + +public class CompensatedSums { + + public static void main(String [] args) { + double naive = 0; + double sequentialStream = 0; + double parallelStream = 0; + double mySequentialStream = 0; + double myParallelStream = 0; + + for (int loop = 0; loop < 100; loop++) { + // sequence of random numbers of varying magnitudes, both positive and negative + double[] rand = new Random().doubles(1_000_000) + .map(Math::log) + .map(x -> (Double.doubleToLongBits(x) % 2 == 0) ? x : -x) + .toArray(); + + // base case: standard Kahan summation + double[] sum = new double[2]; + for (int i=0; i < rand.length; i++) { + sumWithCompensation(sum, rand[i]); + } + + // squared error of naive sum by reduction - should be large + naive += Math.pow(DoubleStream.of(rand).reduce((x, y) -> x+y).getAsDouble() - sum[0], 2); + + // squared error of sequential sum - should be 0 + sequentialStream += Math.pow(DoubleStream.of(rand).sum() - sum[0], 2); + + // squared error of parallel sum + parallelStream += Math.pow(DoubleStream.of(rand).parallel().sum() - sum[0], 2); + + // squared error of modified sequential sum - should be 0 + mySequentialStream += Math.pow(computeFinalSum(DoubleStream.of(rand).collect( + () -> new double[3], + (ll, d) -> { + sumWithCompensation(ll, d); + ll[2] += d; + }, + (ll, rr) -> { + sumWithCompensation(ll, rr[0]); + sumWithCompensation(ll, -rr[1]); // minus is added + ll[2] += rr[2]; + })) - sum[0], 2); + + // squared error of modified parallel sum - typically ~0.25-0.5 times squared error of parallel sum + myParallelStream += Math.pow(computeFinalSum(DoubleStream.of(rand).parallel().collect( + () -> new double[3], + (ll, d) -> { + sumWithCompensation(ll, d); + ll[2] += d; + }, + (ll, rr) -> { + sumWithCompensation(ll, rr[0]); + sumWithCompensation(ll, -rr[1]); // minus is added + ll[2] += rr[2]; + })) - sum[0], 2); + } + + // print sum of squared errors + System.out.println(naive); + System.out.println(sequentialStream); + System.out.println(parallelStream); + System.out.println(mySequentialStream); + System.out.println(myParallelStream); + } + + // from OpenJDK8 Collectors, unmodified + static double[] sumWithCompensation(double[] intermediateSum, double value) { + double tmp = value - intermediateSum[1]; + double sum = intermediateSum[0]; + double velvel = sum + tmp; // Little wolf of rounding error + intermediateSum[1] = (velvel - sum) - tmp; + intermediateSum[0] = velvel; + return intermediateSum; + } + + // from OpenJDK8 Collectors, unmodified + static double computeFinalSum(double[] summands) { + double tmp = summands[0] + summands[1]; + double simpleSum = summands[summands.length - 1]; + if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) + return simpleSum; + else + return tmp; + } + +} \ No newline at end of file diff --git a/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java b/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java new file mode 100644 index 0000000000000..0701792450a4b --- /dev/null +++ b/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8214761 + * @summary When combining two DoubleSummaryStatistics, the compensation + * has to be subtracted. + */ + +import java.util.DoubleSummaryStatistics; + +public class NegativeCompensation { + static final double VAL = 1.000000001; + static final int LOG_ITER = 21; + + public static void main(String[] args) { + DoubleSummaryStatistics stat0 = new DoubleSummaryStatistics(); + DoubleSummaryStatistics stat1 = new DoubleSummaryStatistics(); + DoubleSummaryStatistics stat2 = new DoubleSummaryStatistics(); + + stat1.accept(VAL); + stat1.accept(VAL); + stat2.accept(VAL); + stat2.accept(VAL); + stat2.accept(VAL); + + for (int i = 0; i < LOG_ITER; ++i) { + stat1.combine(stat2); + stat2.combine(stat1); + } + + System.out.println("count: " + stat2.getCount()); + for (long i = 0, iend = stat2.getCount(); i < iend; ++i) { + stat0.accept(VAL); + } + + double res = 0; + for(long i = 0, iend = stat2.getCount(); i < iend; ++i) { + res += VAL; + } + + double absErrN = Math.abs(res - stat2.getSum()); + double absErr = Math.abs(stat0.getSum() - stat2.getSum()); + System.out.println("serial sum: " + stat0.getSum()); + System.out.println("combined sum: " + stat2.getSum()); + System.out.println("abs error: " + absErr); + if (absErr == 0.0) { + throw new RuntimeException("Absolute error is too big: " + absErr); + } + } +} \ No newline at end of file From c13313c52ed11d7abdb66d3ce363996a46a893be Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Mon, 19 Jul 2021 12:50:58 -0500 Subject: [PATCH 3/6] stashing --- .../DoubleStreamSums/CompensatedSums.java | 39 ++++--------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java index 5abb569836304..be0397f14910d 100644 --- a/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java +++ b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java @@ -24,19 +24,21 @@ /* * @test * @bug 8214761 + * @run main CompensatedSums + * @summary */ import java.util.Random; import java.util.stream.DoubleStream; +import static org.testng.Assert.assertTrue; + public class CompensatedSums { public static void main(String [] args) { double naive = 0; double sequentialStream = 0; double parallelStream = 0; - double mySequentialStream = 0; - double myParallelStream = 0; for (int loop = 0; loop < 100; loop++) { // sequence of random numbers of varying magnitudes, both positive and negative @@ -60,39 +62,12 @@ public static void main(String [] args) { // squared error of parallel sum parallelStream += Math.pow(DoubleStream.of(rand).parallel().sum() - sum[0], 2); - // squared error of modified sequential sum - should be 0 - mySequentialStream += Math.pow(computeFinalSum(DoubleStream.of(rand).collect( - () -> new double[3], - (ll, d) -> { - sumWithCompensation(ll, d); - ll[2] += d; - }, - (ll, rr) -> { - sumWithCompensation(ll, rr[0]); - sumWithCompensation(ll, -rr[1]); // minus is added - ll[2] += rr[2]; - })) - sum[0], 2); - - // squared error of modified parallel sum - typically ~0.25-0.5 times squared error of parallel sum - myParallelStream += Math.pow(computeFinalSum(DoubleStream.of(rand).parallel().collect( - () -> new double[3], - (ll, d) -> { - sumWithCompensation(ll, d); - ll[2] += d; - }, - (ll, rr) -> { - sumWithCompensation(ll, rr[0]); - sumWithCompensation(ll, -rr[1]); // minus is added - ll[2] += rr[2]; - })) - sum[0], 2); } // print sum of squared errors - System.out.println(naive); - System.out.println(sequentialStream); - System.out.println(parallelStream); - System.out.println(mySequentialStream); - System.out.println(myParallelStream); + System.out.println("Naive: " + naive); + System.out.println("Sequential Stream: " + sequentialStream); + System.out.println("Paralellel Stream: " + parallelStream); } // from OpenJDK8 Collectors, unmodified From 10b8dcda0646929a04fad9b508b232034234944d Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Wed, 21 Jul 2021 15:13:51 -0500 Subject: [PATCH 4/6] Updates with more test coverage --- .../classes/java/util/stream/Collectors.java | 2 +- .../DoubleStreamSums/CompensatedSums.java | 68 ++++++++++++++++--- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index c1a552e6cfe9c..0f07f871bd853 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -766,7 +766,7 @@ static double[] sumWithCompensation(double[] intermediateSum, double value) { * correctly-signed infinity stored in the simple sum. */ static double computeFinalSum(double[] summands) { - // Better error bounds to add both terms as the final sum + // Final sum with better error bounds subtract second summand as it is negated double tmp = summands[0] - summands[1]; double simpleSum = summands[summands.length - 1]; if (Double.isNaN(tmp) && Double.isInfinite(simpleSum)) diff --git a/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java index be0397f14910d..a4967c268843f 100644 --- a/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java +++ b/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java @@ -24,21 +24,32 @@ /* * @test * @bug 8214761 - * @run main CompensatedSums + * @run testng CompensatedSums * @summary */ import java.util.Random; +import java.util.function.BiConsumer; +import java.util.function.ObjDoubleConsumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.DoubleStream; import static org.testng.Assert.assertTrue; +import org.testng.Assert; +import org.testng.annotations.Test; + public class CompensatedSums { - public static void main(String [] args) { + @Test + public void testCompensatedSums() { double naive = 0; - double sequentialStream = 0; - double parallelStream = 0; + double jdkSequentialStreamError = 0; + double goodSequentialStreamError = 0; + double jdkParallelStreamError = 0; + double goodParallelStreamError = 0; + double badParallelStreamError = 0; for (int loop = 0; loop < 100; loop++) { // sequence of random numbers of varying magnitudes, both positive and negative @@ -53,21 +64,38 @@ public static void main(String [] args) { sumWithCompensation(sum, rand[i]); } + // All error is the squared difference of the standard Kahan Sum vs JDK Stream sum implementation + // Older less accurate implementations included here as the baseline. + // squared error of naive sum by reduction - should be large naive += Math.pow(DoubleStream.of(rand).reduce((x, y) -> x+y).getAsDouble() - sum[0], 2); // squared error of sequential sum - should be 0 - sequentialStream += Math.pow(DoubleStream.of(rand).sum() - sum[0], 2); + jdkSequentialStreamError += Math.pow(DoubleStream.of(rand).sum() - sum[0], 2); + + goodSequentialStreamError += Math.pow(computeFinalSum(DoubleStream.of(rand).collect(doubleSupplier,objDoubleConsumer,goodCollectorConsumer)) - sum[0], 2); + + // squared error of parallel sum from the JDK + jdkParallelStreamError += Math.pow(DoubleStream.of(rand).parallel().sum() - sum[0], 2); // squared error of parallel sum - parallelStream += Math.pow(DoubleStream.of(rand).parallel().sum() - sum[0], 2); + goodParallelStreamError += Math.pow(computeFinalSum(DoubleStream.of(rand).parallel().collect(doubleSupplier,objDoubleConsumer,goodCollectorConsumer)) - sum[0], 2); + + // the bad parallel stream + badParallelStreamError += Math.pow(computeFinalSum(DoubleStream.of(rand).parallel().collect(doubleSupplier,objDoubleConsumer,badCollectorConsumer)) - sum[0], 2); + } - // print sum of squared errors - System.out.println("Naive: " + naive); - System.out.println("Sequential Stream: " + sequentialStream); - System.out.println("Paralellel Stream: " + parallelStream); + Assert.assertEquals(goodSequentialStreamError, 0.0); + Assert.assertEquals(goodSequentialStreamError, jdkSequentialStreamError); + + Assert.assertTrue(jdkParallelStreamError <= goodParallelStreamError); + Assert.assertTrue(badParallelStreamError > goodParallelStreamError); + + Assert.assertTrue(naive > jdkSequentialStreamError); + Assert.assertTrue(naive > jdkParallelStreamError); + } // from OpenJDK8 Collectors, unmodified @@ -90,4 +118,24 @@ static double computeFinalSum(double[] summands) { return tmp; } + //Suppliers and consumers for Double Stream summation collection. + static Supplier doubleSupplier = () -> new double[3]; + static ObjDoubleConsumer objDoubleConsumer = (double[] ll, double d) -> { + sumWithCompensation(ll, d); + ll[2] += d; + }; + static BiConsumer badCollectorConsumer = + (ll, rr) -> { + sumWithCompensation(ll, rr[0]); + sumWithCompensation(ll, rr[1]); + ll[2] += rr[2]; + }; + + static BiConsumer goodCollectorConsumer = + (ll, rr) -> { + sumWithCompensation(ll, rr[0]); + sumWithCompensation(ll, -rr[1]); + ll[2] += rr[2]; + }; + } \ No newline at end of file From 905450d860ff650e455093635bbc68e5180550a6 Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Tue, 31 Aug 2021 19:29:27 -0500 Subject: [PATCH 5/6] Changing some comments. --- .../share/classes/java/util/DoubleSummaryStatistics.java | 2 +- src/java.base/share/classes/java/util/stream/Collectors.java | 4 ++-- .../share/classes/java/util/stream/DoublePipeline.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java index afec33a6114d0..06cd7aadfe2c8 100644 --- a/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java +++ b/src/java.base/share/classes/java/util/DoubleSummaryStatistics.java @@ -157,7 +157,7 @@ public void combine(DoubleSummaryStatistics other) { simpleSum += other.simpleSum; sumWithCompensation(other.sum); - //Negating this value because low-order bits are in negated form + // Subtract compensation bits sumWithCompensation(-other.sumCompensation); min = Math.min(min, other.min); max = Math.max(max, other.max); diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java index 0f07f871bd853..94780d2ae1629 100644 --- a/src/java.base/share/classes/java/util/stream/Collectors.java +++ b/src/java.base/share/classes/java/util/stream/Collectors.java @@ -734,7 +734,7 @@ public static Collector collectingAndThen(Collector dow a[2] += val;}, (a, b) -> { sumWithCompensation(a, b[0]); a[2] += b[2]; - //Negating this value because low-order bits are in negated form + // Subtract compensation bits return sumWithCompensation(a, -b[1]); }, a -> computeFinalSum(a), CH_NOID); @@ -849,7 +849,7 @@ static double computeFinalSum(double[] summands) { (a, t) -> { double val = mapper.applyAsDouble(t); sumWithCompensation(a, val); a[2]++; a[3]+= val;}, (a, b) -> { sumWithCompensation(a, b[0]); - //Negating this value because low-order bits are in negated form + // Subtract compensation bits sumWithCompensation(a, -b[1]); a[2] += b[2]; a[3] += b[3]; return a; diff --git a/src/java.base/share/classes/java/util/stream/DoublePipeline.java b/src/java.base/share/classes/java/util/stream/DoublePipeline.java index 9315c00c6d520..9771440f8aa93 100644 --- a/src/java.base/share/classes/java/util/stream/DoublePipeline.java +++ b/src/java.base/share/classes/java/util/stream/DoublePipeline.java @@ -454,7 +454,7 @@ public final double sum() { }, (ll, rr) -> { Collectors.sumWithCompensation(ll, rr[0]); - //Negating this value because low-order bits are in negated form + // Subtract compensation bits Collectors.sumWithCompensation(ll, -rr[1]); ll[2] += rr[2]; }); @@ -498,7 +498,7 @@ public final OptionalDouble average() { }, (ll, rr) -> { Collectors.sumWithCompensation(ll, rr[0]); - //Negating this value because low-order bits are in negated form + // Subtract compensation bits Collectors.sumWithCompensation(ll, -rr[1]); ll[2] += rr[2]; ll[3] += rr[3]; From 722826fc9b3a3ca4dd39c24cce09021c9757b9c0 Mon Sep 17 00:00:00 2001 From: Ian Graves Date: Wed, 1 Sep 2021 14:33:47 -0500 Subject: [PATCH 6/6] Fixing compensation test --- .../NegativeCompensation.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java b/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java index 0701792450a4b..b43db37794c8f 100644 --- a/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java +++ b/test/jdk/java/util/DoubleSummaryStatistics/NegativeCompensation.java @@ -24,17 +24,22 @@ /* * @test * @bug 8214761 + * @run testng NegativeCompensation * @summary When combining two DoubleSummaryStatistics, the compensation * has to be subtracted. */ import java.util.DoubleSummaryStatistics; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class NegativeCompensation { static final double VAL = 1.000000001; static final int LOG_ITER = 21; - public static void main(String[] args) { + @Test + public static void testErrorComparision() { DoubleSummaryStatistics stat0 = new DoubleSummaryStatistics(); DoubleSummaryStatistics stat1 = new DoubleSummaryStatistics(); DoubleSummaryStatistics stat2 = new DoubleSummaryStatistics(); @@ -50,7 +55,6 @@ public static void main(String[] args) { stat2.combine(stat1); } - System.out.println("count: " + stat2.getCount()); for (long i = 0, iend = stat2.getCount(); i < iend; ++i) { stat0.accept(VAL); } @@ -62,11 +66,8 @@ public static void main(String[] args) { double absErrN = Math.abs(res - stat2.getSum()); double absErr = Math.abs(stat0.getSum() - stat2.getSum()); - System.out.println("serial sum: " + stat0.getSum()); - System.out.println("combined sum: " + stat2.getSum()); - System.out.println("abs error: " + absErr); - if (absErr == 0.0) { - throw new RuntimeException("Absolute error is too big: " + absErr); - } + assertTrue(absErrN >= absErr, + "Naive sum error is not greater than or equal to Summary sum"); + assertEquals(absErr, 0.0, "Absolute error is not zero"); } } \ No newline at end of file