/* * Copyright 2015 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package java.util; import java.util.function.DoubleConsumer; /** * See <a * href="https://docs.oracle.com/javase/8/docs/api/java/util/DoubleSummaryStatistics.html">the * official Java API doc</a> for details. */ public class DoubleSummaryStatistics implements DoubleConsumer { private long count; private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private double sumError; // Because of Kahan summation compensation a naive summation is required // to keep track of infinity values correctly. private double naiveSum; @Override public void accept(double value) { count++; min = Math.min(min, value); max = Math.max(max, value); naiveSum += value; sum(value); } public void combine(DoubleSummaryStatistics other) { count += other.count; min = Math.min(min, other.min); max = Math.max(max, other.max); naiveSum += other.naiveSum; sum(other.sum); sum(other.sumError); } public double getAverage() { return count > 0 ? getSum() / count : 0d; } public long getCount() { return count; } public double getMin() { return min; } public double getMax() { return max; } public double getSum() { // Adding sum and sumError here to get a better result // because Kahan summation always applies error compensation // on the next summation. double compensatedSum = sum + sumError; // sumError can be NaN if infinity values had been accepted. if (Double.isNaN(compensatedSum) && Double.isInfinite(naiveSum)) { return naiveSum; } return compensatedSum; } @Override public String toString() { return "DoubleSummaryStatistics[" + "count = " + count + ", avg = " + getAverage() + ", min = " + min + ", max = " + max + ", sum = " + getSum() + "]"; } /** * Adds a new value to the current sum using Kahan summation * algorithm for improved summation precision. * * https://en.wikipedia.org/wiki/Kahan_summation_algorithm * * @param value the value being added to the sum */ private void sum(double value) { double compensatedValue = value - sumError; double newSum = sum + compensatedValue; // Logically 'summationError' always evaluates to 0 // but in reality it contains a small summation error // that can occur because of rounding in floating point arithmetic. // For example 1.0 + EPSILON with EPSILON being half machine precision // (basically Math.ulp(1.0)/2) will result in 1.0. // Tests should verify that GWT compiler / Closure compiler do not // remove this line during optimizations. // NOTE: sumError can become NaN for infinity values. sumError = (newSum - sum) - compensatedValue; sum = newSum; } }