rev 55098 : [mq]: 8224716-Javadoc-of-Int-Long-DoubleSummaryStatistics-should-mention-possible-overflow-of-count
1 /*
2 * Copyright (c) 2012, 2017, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.util;
26
27 import java.util.function.DoubleConsumer;
28 import java.util.stream.Collector;
29 import java.util.stream.DoubleStream;
30
31 /**
32 * A state object for collecting statistics such as count, min, max, sum, and
33 * average.
34 *
35 * <p>This class is designed to work with (though does not require)
36 * {@linkplain java.util.stream streams}. For example, you can compute
37 * summary statistics on a stream of doubles with:
38 * <pre> {@code
39 * DoubleSummaryStatistics stats = doubleStream.collect(DoubleSummaryStatistics::new,
40 * DoubleSummaryStatistics::accept,
41 * DoubleSummaryStatistics::combine);
42 * }</pre>
43 *
44 * <p>{@code DoubleSummaryStatistics} can be used as a
45 * {@linkplain java.util.stream.Stream#collect(Collector) reduction}
46 * target for a {@linkplain java.util.stream.Stream stream}. For example:
47 *
48 * <pre> {@code
49 * DoubleSummaryStatistics stats = people.stream()
50 * .collect(Collectors.summarizingDouble(Person::getWeight));
51 *}</pre>
52 *
53 * This computes, in a single pass, the count of people, as well as the minimum,
54 * maximum, sum, and average of their weights.
55 *
56 * @implNote This implementation is not thread safe. However, it is safe to use
57 * {@link java.util.stream.Collectors#summarizingDouble(java.util.function.ToDoubleFunction)
58 * Collectors.summarizingDouble()} on a parallel stream, because the parallel
59 * implementation of {@link java.util.stream.Stream#collect Stream.collect()}
60 * provides the necessary partitioning, isolation, and merging of results for
61 * safe and efficient parallel execution.
62 * @since 1.8
63 */
64 public class DoubleSummaryStatistics implements DoubleConsumer {
65 private long count;
66 private double sum;
67 private double sumCompensation; // Low order bits of sum
68 private double simpleSum; // Used to compute right sum for non-finite inputs
69 private double min = Double.POSITIVE_INFINITY;
70 private double max = Double.NEGATIVE_INFINITY;
71
72 /**
73 * Constructs an empty instance with zero count, zero sum,
74 * {@code Double.POSITIVE_INFINITY} min, {@code Double.NEGATIVE_INFINITY}
75 * max and zero average.
76 */
77 public DoubleSummaryStatistics() { }
78
79 /**
80 * Constructs a non-empty instance with the specified {@code count},
81 * {@code min}, {@code max}, and {@code sum}.
82 *
83 * <p>If {@code count} is zero then the remaining arguments are ignored and
84 * an empty instance is constructed.
85 *
86 * <p>If the arguments are inconsistent then an {@code IllegalArgumentException}
87 * is thrown. The necessary consistent argument conditions are:
88 * <ul>
89 * <li>{@code count >= 0}</li>
90 * <li>{@code (min <= max && !isNaN(sum)) || (isNaN(min) && isNaN(max) && isNaN(sum))}</li>
91 * </ul>
92 * @apiNote
93 * The enforcement of argument correctness means that the retrieved set of
94 * recorded values obtained from a {@code DoubleSummaryStatistics} source
95 * instance may not be a legal set of arguments for this constructor due to
96 * arithmetic overflow of the source's recorded count of values.
97 * The consistent argument conditions are not sufficient to prevent the
98 * creation of an internally inconsistent instance. An example of such a
99 * state would be an instance with: {@code count} = 2, {@code min} = 1,
100 * {@code max} = 2, and {@code sum} = 0.
101 *
102 * @param count the count of values
103 * @param min the minimum value
104 * @param max the maximum value
105 * @param sum the sum of all values
106 * @throws IllegalArgumentException if the arguments are inconsistent
107 * @since 10
108 */
109 public DoubleSummaryStatistics(long count, double min, double max, double sum)
110 throws IllegalArgumentException {
111 if (count < 0L) {
112 throw new IllegalArgumentException("Negative count value");
113 } else if (count > 0L) {
114 if (min > max)
115 throw new IllegalArgumentException("Minimum greater than maximum");
116
117 // All NaN or non NaN
118 var ncount = DoubleStream.of(min, max, sum).filter(Double::isNaN).count();
119 if (ncount > 0 && ncount < 3)
120 throw new IllegalArgumentException("Some, not all, of the minimum, maximum, or sum is NaN");
121
122 this.count = count;
123 this.sum = sum;
124 this.simpleSum = sum;
125 this.sumCompensation = 0.0d;
126 this.min = min;
127 this.max = max;
128 }
129 // Use default field values if count == 0
130 }
131
132 /**
133 * Records another value into the summary information.
134 *
135 * @param value the input value
136 */
137 @Override
138 public void accept(double value) {
139 ++count;
140 simpleSum += value;
141 sumWithCompensation(value);
142 min = Math.min(min, value);
143 max = Math.max(max, value);
144 }
145
146 /**
147 * Combines the state of another {@code DoubleSummaryStatistics} into this
148 * one.
149 *
150 * @param other another {@code DoubleSummaryStatistics}
151 * @throws NullPointerException if {@code other} is null
152 */
153 public void combine(DoubleSummaryStatistics other) {
154 count += other.count;
155 simpleSum += other.simpleSum;
156 sumWithCompensation(other.sum);
157 sumWithCompensation(other.sumCompensation);
158 min = Math.min(min, other.min);
159 max = Math.max(max, other.max);
160 }
161
162 /**
163 * Incorporate a new double value using Kahan summation /
164 * compensated summation.
165 */
166 private void sumWithCompensation(double value) {
167 double tmp = value - sumCompensation;
168 double velvel = sum + tmp; // Little wolf of rounding error
169 sumCompensation = (velvel - sum) - tmp;
170 sum = velvel;
171 }
172
173 /**
174 * Return the count of values recorded.
175 *
176 * @return the count of values
177 */
178 public final long getCount() {
179 return count;
180 }
181
182 /**
183 * Returns the sum of values recorded, or zero if no values have been
184 * recorded.
185 *
186 * <p> The value of a floating-point sum is a function both of the
187 * input values as well as the order of addition operations. The
188 * order of addition operations of this method is intentionally
189 * not defined to allow for implementation flexibility to improve
190 * the speed and accuracy of the computed result.
191 *
192 * In particular, this method may be implemented using compensated
193 * summation or other technique to reduce the error bound in the
194 * numerical sum compared to a simple summation of {@code double}
195 * values.
196 *
197 * Because of the unspecified order of operations and the
198 * possibility of using differing summation schemes, the output of
199 * this method may vary on the same input values.
200 *
201 * <p>Various conditions can result in a non-finite sum being
202 * computed. This can occur even if the all the recorded values
203 * being summed are finite. If any recorded value is non-finite,
204 * the sum will be non-finite:
205 *
206 * <ul>
207 *
208 * <li>If any recorded value is a NaN, then the final sum will be
209 * NaN.
210 *
211 * <li>If the recorded values contain one or more infinities, the
212 * sum will be infinite or NaN.
213 *
214 * <ul>
215 *
216 * <li>If the recorded values contain infinities of opposite sign,
217 * the sum will be NaN.
218 *
219 * <li>If the recorded values contain infinities of one sign and
220 * an intermediate sum overflows to an infinity of the opposite
221 * sign, the sum may be NaN.
222 *
223 * </ul>
224 *
225 * </ul>
226 *
227 * It is possible for intermediate sums of finite values to
228 * overflow into opposite-signed infinities; if that occurs, the
229 * final sum will be NaN even if the recorded values are all
230 * finite.
231 *
232 * If all the recorded values are zero, the sign of zero is
233 * <em>not</em> guaranteed to be preserved in the final sum.
234 *
235 * @apiNote Values sorted by increasing absolute magnitude tend to yield
236 * more accurate results.
237 *
238 * @return the sum of values, or zero if none
239 */
240 public final double getSum() {
241 // Better error bounds to add both terms as the final sum
242 double tmp = sum + sumCompensation;
243 if (Double.isNaN(tmp) && Double.isInfinite(simpleSum))
244 // If the compensated sum is spuriously NaN from
245 // accumulating one or more same-signed infinite values,
246 // return the correctly-signed infinity stored in
247 // simpleSum.
248 return simpleSum;
249 else
250 return tmp;
251 }
252
253 /**
254 * Returns the minimum recorded value, {@code Double.NaN} if any recorded
255 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were
256 * recorded. Unlike the numerical comparison operators, this method
257 * considers negative zero to be strictly smaller than positive zero.
258 *
259 * @return the minimum recorded value, {@code Double.NaN} if any recorded
260 * value was NaN or {@code Double.POSITIVE_INFINITY} if no values were
261 * recorded
262 */
263 public final double getMin() {
264 return min;
265 }
266
267 /**
268 * Returns the maximum recorded value, {@code Double.NaN} if any recorded
269 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were
270 * recorded. Unlike the numerical comparison operators, this method
271 * considers negative zero to be strictly smaller than positive zero.
272 *
273 * @return the maximum recorded value, {@code Double.NaN} if any recorded
274 * value was NaN or {@code Double.NEGATIVE_INFINITY} if no values were
275 * recorded
276 */
277 public final double getMax() {
278 return max;
279 }
280
281 /**
282 * Returns the arithmetic mean of values recorded, or zero if no
283 * values have been recorded.
284 *
285 * <p> The computed average can vary numerically and have the
286 * special case behavior as computing the sum; see {@link #getSum}
287 * for details.
288 *
289 * @apiNote Values sorted by increasing absolute magnitude tend to yield
290 * more accurate results.
291 *
292 * @return the arithmetic mean of values, or zero if none
293 */
294 public final double getAverage() {
295 return getCount() > 0 ? getSum() / getCount() : 0.0d;
296 }
297
298 /**
299 * Returns a non-empty string representation of this object suitable for
300 * debugging. The exact presentation format is unspecified and may vary
301 * between implementations and versions.
302 */
303 @Override
304 public String toString() {
305 return String.format(
306 "%s{count=%d, sum=%f, min=%f, average=%f, max=%f}",
307 this.getClass().getSimpleName(),
308 getCount(),
309 getSum(),
310 getMin(),
311 getAverage(),
312 getMax());
313 }
314 }
--- EOF ---