Skip to content

Commit

Permalink
ISPN-6021 Merge DoubleSum and DoubleAvg classes
Browse files Browse the repository at this point in the history
  • Loading branch information
anistor committed Dec 11, 2015
1 parent 476664d commit 11ca7e1
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 177 deletions.
Expand Up @@ -4,7 +4,7 @@
* Computes the average of {@link Number}s. The output type is always a {@link Double}. Nulls are excluded from the
* computation. If there are no remaining non-null values to which the aggregate function can be applied, the result of
* the aggregate function is {@code null}.
* <p/>
* <p>
* 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.
*
Expand All @@ -22,28 +22,28 @@ protected AvgAccumulator(int inPos, int outPos, Class<?> fieldType) {

@Override
public void init(Object[] accRow) {
accRow[outPos] = new DoubleAvg();
accRow[outPos] = new DoubleStat();
}

@Override
public void update(Object[] accRow, Object value) {
if (value != null) {
((DoubleAvg) accRow[outPos]).update(((Number) value).doubleValue());
((DoubleStat) accRow[outPos]).update(((Number) value).doubleValue());
}
}

@Override
protected void merge(Object[] accRow, Object value) {
if (value instanceof DoubleAvg) {
DoubleAvg avgVal = (DoubleAvg) value;
((DoubleAvg) accRow[outPos]).update(avgVal.getSum(), avgVal.getCount());
if (value instanceof DoubleStat) {
DoubleStat avgVal = (DoubleStat) value;
((DoubleStat) accRow[outPos]).update(avgVal.getSum(), avgVal.getCount());
} else {
update(accRow, value);
}
}

@Override
protected void finish(Object[] accRow) {
accRow[outPos] = ((DoubleAvg) accRow[outPos]).getAvg();
accRow[outPos] = ((DoubleStat) accRow[outPos]).getAvg();
}
}

This file was deleted.

Expand Up @@ -2,20 +2,26 @@


/**
* Computes the sum of doubles. The implementation uses compensated summation in order to reduce the error bound in the
* 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
*/
class DoubleSum {
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.
Expand All @@ -32,6 +38,9 @@ void update(double value) {
* @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)) {
Expand All @@ -43,4 +52,19 @@ Double getSum() {
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;
}
}
Expand Up @@ -28,7 +28,7 @@ protected SumAccumulator(int inPos, int outPos, Class<?> fieldType) {
@Override
public void init(Object[] accRow) {
if (fieldType == Double.class || fieldType == Float.class) {
accRow[outPos] = new DoubleSum();
accRow[outPos] = new DoubleStat();
}
}

Expand All @@ -37,7 +37,7 @@ public void update(Object[] accRow, Object val) {
if (val != null) {
Number value = (Number) val;
if (fieldType == Double.class || fieldType == Float.class) {
((DoubleSum) accRow[outPos]).update(value.doubleValue());
((DoubleStat) accRow[outPos]).update(value.doubleValue());
} else if (fieldType == Long.class || fieldType == Integer.class || fieldType == Byte.class || fieldType == Short.class) {
value = value.longValue();
Number sum = (Number) accRow[outPos];
Expand All @@ -60,16 +60,16 @@ public void update(Object[] accRow, Object val) {

@Override
protected void merge(Object[] accRow, Object value) {
if (value instanceof DoubleSum) {
value = ((DoubleSum) value).getSum();
if (value instanceof DoubleStat) {
value = ((DoubleStat) value).getSum();
}
update(accRow, value);
}

@Override
protected void finish(Object[] accRow) {
if (fieldType == Double.class || fieldType == Float.class) {
accRow[outPos] = ((DoubleSum) accRow[outPos]).getSum();
accRow[outPos] = ((DoubleStat) accRow[outPos]).getSum();
}
}

Expand Down

This file was deleted.

@@ -0,0 +1,109 @@
package org.infinispan.objectfilter.impl.aggregation;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

/**
* @author anistor@redhat.com
* @since 8.1
*/
public class DoubleStatTest {

private static final double DELTA = 0.0000001d;

@Test
public void testEmptySum() throws Exception {
DoubleStat sum = new DoubleStat();
assertNull(sum.getSum());
}

@Test
public void testEmptyAvg() throws Exception {
DoubleStat avg = new DoubleStat();
assertNull(avg.getAvg());
}

@Test
public void testSum() throws Exception {
DoubleStat sum = new DoubleStat();
sum.update(10);
sum.update(20);
assertEquals(30.0d, sum.getSum(), DELTA);
}

@Test
public void testAvg() throws Exception {
DoubleStat avg = new DoubleStat();
avg.update(10);
avg.update(20);
assertEquals(15.0d, avg.getAvg(), DELTA);
}

@Test
public void testSumWithNaN() throws Exception {
DoubleStat sum = new DoubleStat();
sum.update(10);
sum.update(Double.NaN);
assertEquals(Double.NaN, sum.getSum(), DELTA);
}

@Test
public void testAvgWithNaN() throws Exception {
DoubleStat avg = new DoubleStat();
avg.update(10);
avg.update(Double.NaN);
assertEquals(Double.NaN, avg.getAvg(), DELTA);
}

@Test
public void testSumWithPlusInf() throws Exception {
DoubleStat sum = new DoubleStat();
sum.update(10);
sum.update(Double.POSITIVE_INFINITY);
assertEquals(Double.POSITIVE_INFINITY, sum.getSum(), DELTA);
}

@Test
public void testAvgWithPlusInf() throws Exception {
DoubleStat avg = new DoubleStat();
avg.update(10);
avg.update(Double.POSITIVE_INFINITY);
assertEquals(Double.POSITIVE_INFINITY, avg.getAvg(), DELTA);
}

@Test
public void testSumWithMinusInf() throws Exception {
DoubleStat sum = new DoubleStat();
sum.update(10);
sum.update(Double.NEGATIVE_INFINITY);
assertEquals(Double.NEGATIVE_INFINITY, sum.getSum(), DELTA);
}

@Test
public void testAvgWithMinusInf() throws Exception {
DoubleStat avg = new DoubleStat();
avg.update(10);
avg.update(Double.NEGATIVE_INFINITY);
assertEquals(Double.NEGATIVE_INFINITY, avg.getAvg(), DELTA);
}

@Test
public void testSumWithMinusInfAndPlusInf() throws Exception {
DoubleStat sum = new DoubleStat();
sum.update(10);
sum.update(Double.NEGATIVE_INFINITY);
sum.update(Double.POSITIVE_INFINITY);
assertEquals(Double.NaN, sum.getSum(), DELTA);
}

@Test
public void testAvgWithMinusInfAndPlusInf() throws Exception {
DoubleStat avg = new DoubleStat();
avg.update(10);
avg.update(Double.NEGATIVE_INFINITY);
avg.update(Double.POSITIVE_INFINITY);
assertEquals(Double.NaN, avg.getAvg(), DELTA);
}
}

0 comments on commit 11ca7e1

Please sign in to comment.