package org.infinispan.objectfilter.impl.aggregation;
/**
* Computes the sum and average of doubles. The implementation uses compensated summation in order to reduce the error bound in the
* numerical sum compared to a simple summation of {@code double} values, similar to the way {@link
* java.util.DoubleSummaryStatistics} works.
*
* @author anistor@redhat.com
* @since 8.1
*/
final class DoubleStat {
private long count;
private double sum;
private double sumCompensation; // Low order bits of sum
private double simpleSum; // Used to compute right sum for non-finite inputs
void update(double value) {
update(value, 1);
}
void update(double value, long count) {
this.count += count;
simpleSum += value;
// Incorporate a new double value using Kahan summation / compensated summation.
double tmp = value - sumCompensation;
double velvel = sum + tmp; // Little wolf of rounding error
sumCompensation = (velvel - sum) - tmp;
sum = velvel;
}
/**
* Returns the sum of seen values. If any value is a NaN or the sum is at any point a NaN then the average will be
* NaN. The average returned can vary depending upon the order in which values are seen.
*
* @return the sum of values
*/
Double getSum() {
if (count == 0) {
return null;
}
// Better error bounds to add both terms as the final sum
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,
// return the correctly-signed infinity stored in simpleSum.
return simpleSum;
} else {
return tmp;
}
}
/**
* Returns the arithmetic mean of seen values, or null if no values have been seen. If any value is a NaN or the sum
* is at any point a NaN then the average will be NaN. The average returned can vary depending upon the order in
* which values are seen.
*
* @return the arithmetic mean of values, or null if none
*/
Double getAvg() {
return count == 0 ? null : getSum() / count;
}
long getCount() {
return count;
}
}