This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git

commit dec22784a51aeb0899750a69f81736f2bde0f001
Author: Alex Herbert <aherb...@apache.org>
AuthorDate: Wed Dec 27 22:59:42 2023 +0000

    Refactor division for the integer mean
---
 .../commons/statistics/descriptive/IntMath.java    | 20 ++++++++++++++
 .../commons/statistics/descriptive/IntMean.java    |  5 ++--
 .../commons/statistics/descriptive/LongMean.java   |  5 ++--
 .../statistics/descriptive/IntMathTest.java        | 31 ++++++++++++++++++++++
 .../commons/statistics/descriptive/TestHelper.java |  2 ++
 src/conf/checkstyle/checkstyle-suppressions.xml    |  2 +-
 6 files changed, 58 insertions(+), 7 deletions(-)

diff --git 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
index 2309750..fd5c164 100644
--- 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
+++ 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMath.java
@@ -18,6 +18,7 @@ package org.apache.commons.statistics.descriptive;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import org.apache.commons.numbers.core.DD;
 
 /**
  * Support class for integer math.
@@ -35,6 +36,8 @@ final class IntMath {
     private static final int EXP_SHIFT = 52;
     /** 0.5. */
     private static final double HALF = 0.5;
+    /** 2^53. */
+    private static final long TWO_POW_53 = 1L << 53;
 
     /** No instances. */
     private IntMath() {}
@@ -388,4 +391,21 @@ final class IntMath {
         }
         return y;
     }
+
+    /**
+     * Divide value {@code x} by the count {@code n}.
+     *
+     * @param x Value.
+     * @param n Count.
+     * @return the quotient
+     */
+    static double divide(Int128 x, long n) {
+        final DD a = x.toDD();
+        if (n < TWO_POW_53) {
+            // n is a representable double
+            return a.divide(n).doubleValue();
+        }
+        // Extended precision divide when n > 2^53
+        return a.divide(DD.of(n)).doubleValue();
+    }
 }
diff --git 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMean.java
 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMean.java
index 0713047..9bd384d 100644
--- 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMean.java
+++ 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/IntMean.java
@@ -130,9 +130,8 @@ public final class IntMean implements IntStatistic, 
StatisticAccumulator<IntMean
         if (n < SMALL_N) {
             return (double) sum.lo64() / n;
         }
-        // Extended precision.
-        // Could divide by DD.of(n) when |n| > 2^53.
-        return sum.toDD().divide(n).doubleValue();
+        // Extended precision
+        return IntMath.divide(sum, n);
     }
 
     @Override
diff --git 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMean.java
 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMean.java
index 3f84a9e..37b2095 100644
--- 
a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMean.java
+++ 
b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/LongMean.java
@@ -127,9 +127,8 @@ public final class LongMean implements LongStatistic, 
StatisticAccumulator<LongM
         if (sum.hi64() == 0 && Math.abs(sum.lo64()) < SMALL_SUM) {
             return (double) sum.lo64() / n;
         }
-        // Extended precision.
-        // Could divide by DD.of(n) when |n| > 2^53.
-        return sum.toDD().divide(n).doubleValue();
+        // Extended precision
+        return IntMath.divide(sum, n);
     }
 
     @Override
diff --git 
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
index ccc4416..b16baff 100644
--- 
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
+++ 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/IntMathTest.java
@@ -17,13 +17,18 @@
 
 package org.apache.commons.statistics.descriptive;
 
+import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.math.MathContext;
+import java.util.Arrays;
 import java.util.function.Supplier;
 import java.util.stream.DoubleStream;
 import java.util.stream.LongStream;
 import java.util.stream.LongStream.Builder;
 import java.util.stream.Stream;
 import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.statistics.distribution.DoubleTolerances;
+import org.apache.commons.statistics.distribution.TestUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -203,4 +208,30 @@ class IntMathTest {
         Assertions.assertEquals(0, Math.toIntExact(Math.round(x)));
         Assertions.assertThrowsExactly(ArithmeticException.class, () -> 
IntMath.toIntExact(x));
     }
+
+    @ParameterizedTest
+    @MethodSource
+    void testInt128Divide(long a, long b, long n) {
+        final Int128 x = new Int128(a, b);
+        final BigInteger bi = 
BigInteger.valueOf(a).shiftLeft(Long.SIZE).add(BigInteger.valueOf(b));
+        final double expected = new BigDecimal(bi).divide(
+            new BigDecimal(n), MathContext.DECIMAL128).doubleValue();
+        // This may require a 1 ulp tolerance; log the seed to allow repeats
+        TestUtils.assertEquals(expected, IntMath.divide(x, n), 
DoubleTolerances.equals(),
+            () -> String.format("(2^64 * %d + %d) / %d; Seed=%s",
+                a, b, n, Arrays.toString(TestHelper.createRNGSeed())));
+    }
+
+    static Stream<Arguments> testInt128Divide() {
+        final UniformRandomProvider rng = 
TestHelper.createRNG(TestHelper.createRNGSeed());
+        final Stream.Builder<Arguments> builder = Stream.builder();
+        for (int i = 0; i < 100; i++) {
+            final long a = rng.nextLong();
+            final long b = rng.nextLong();
+            final long n = rng.nextLong();
+            builder.accept(Arguments.of(a, b, n >>> 1));
+            builder.accept(Arguments.of(a, b, n >>> 43));
+        }
+        return builder.build();
+    }
 }
diff --git 
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
index 22c385d..a1a57e5 100644
--- 
a/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
+++ 
b/commons-statistics-descriptive/src/test/java/org/apache/commons/statistics/descriptive/TestHelper.java
@@ -218,6 +218,8 @@ final class TestHelper {
 
     /**
      * Creates a seed for the RNG.
+     * This is the same seed within an invocation of the JVM; seeds will be 
different
+     * across JVM instances.
      *
      * @return the seed
      * @see #createRNG(long[])
diff --git a/src/conf/checkstyle/checkstyle-suppressions.xml 
b/src/conf/checkstyle/checkstyle-suppressions.xml
index c363f46..dd425bc 100644
--- a/src/conf/checkstyle/checkstyle-suppressions.xml
+++ b/src/conf/checkstyle/checkstyle-suppressions.xml
@@ -41,5 +41,5 @@
   <suppress checks="MethodLength" 
files=".*[/\\]WilcoxonSignedRankTestTest.java" />
   <suppress checks="IllegalCatch" files=".*[/\\]TestHelper.java" 
lines="390-450" />
   <suppress checks="IllegalCatch" files=".*[/\\]BaseStatisticTest.java" 
lines="280-400" />
-  <suppress checks="IllegalCatch" files=".*[/\\]IntMathTest.java" 
lines="165-175" />
+  <suppress checks="IllegalCatch" files=".*[/\\]IntMathTest.java" 
lines="170-185" />
 </suppressions>

Reply via email to