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

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


The following commit(s) were added to refs/heads/master by this push:
     new 1d9cddcc STATISTICS-98: Check the p-value is valid before significance 
test
1d9cddcc is described below

commit 1d9cddccf47a0230a6f57e24e67999dd1046ef8e
Author: Alex Herbert <[email protected]>
AuthorDate: Thu Aug 27 14:23:40 2026 +0100

    STATISTICS-98: Check the p-value is valid before significance test
---
 .../commons/statistics/inference/Arguments.java    | 14 ++++++++++
 .../statistics/inference/SignificanceResult.java   |  8 ++++--
 .../statistics/inference/ArgumentsTest.java        | 15 ++++++++++
 .../inference/SignificanceResultTest.java          | 19 ++++++++++++-
 .../commons/statistics/inference/TTestTest.java    | 32 ++++++++++++++++++++++
 src/changes/changes.xml                            |  5 ++++
 6 files changed, 90 insertions(+), 3 deletions(-)

diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
index b15ddb4f..c054e52b 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/Arguments.java
@@ -45,6 +45,20 @@ final class Arguments {
         throw new InferenceException(InferenceException.INVALID_SIGNIFICANCE, 
alpha);
     }
 
+    /**
+     * Check the probability {@code p} is in the interval {@code [0, 1]}.
+     *
+     * @param p Probability
+     * @throws IllegalArgumentException if {@code p < 0} or {@code p > 1}
+     */
+    static void checkProbability(double p) {
+        if (p >= 0 && p <= 1) {
+            return;
+        }
+        // Out-of-range or NaN
+        throw new InferenceException(InferenceException.INVALID_PROBABILITY, 
p);
+    }
+
     /**
      * Check that the value is {@code >= 0}.
      *
diff --git 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
index b8ac9045..c1b36e3a 100644
--- 
a/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
+++ 
b/commons-statistics-inference/src/main/java/org/apache/commons/statistics/inference/SignificanceResult.java
@@ -47,10 +47,14 @@ public interface SignificanceResult {
      *
      * @param alpha Significance level of the test.
      * @return true iff null hypothesis can be rejected with confidence {@code 
1 - alpha}
-     * @throws IllegalArgumentException if {@code alpha} is not in the range 
{@code (0, 0.5]}.
+     * @throws IllegalArgumentException if {@code alpha} is not in the 
interval {@code (0, 0.5]};
+     * or if the computed {@code p} is not in the interval {@code [0, 1]}
+     * @see #getPValue()
      */
     default boolean reject(double alpha) {
         Arguments.checkSignificance(alpha);
-        return getPValue() < alpha;
+        final double p = getPValue();
+        Arguments.checkProbability(p);
+        return p < alpha;
     }
 }
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
index 8dbc0a22..792f06a2 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/ArgumentsTest.java
@@ -36,6 +36,21 @@ class ArgumentsTest {
         
Assertions.assertTrue(ex.getMessage().contains(Double.toString(alpha)));
     }
 
+    @ParameterizedTest
+    @ValueSource(doubles = {-0.0, 0.0, Double.MIN_VALUE, 1 - 0x1.0p-53, 1})
+    void testCheckProbability(double p) {
+        Assertions.assertDoesNotThrow(() -> Arguments.checkProbability(p), () 
-> Double.toString(p));
+    }
+
+    @ParameterizedTest
+    @ValueSource(doubles = {-Double.MIN_VALUE, -0.1, -1, -2, 1.0 + 0x1.0p-52, 
1.1, -Double.MAX_VALUE,
+        Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN})
+    void testCheckProbabilityThrows(double p) {
+        final IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class,
+            () -> Arguments.checkProbability(p), () -> Double.toString(p));
+        Assertions.assertTrue(ex.getMessage().contains(Double.toString(p)));
+    }
+
     @ParameterizedTest
     @ValueSource(ints = {Integer.MIN_VALUE, -1})
     void testCheckNonNegativeIntThrows(double v) {
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
index 83eff026..0f42ddd8 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/SignificanceResultTest.java
@@ -40,7 +40,7 @@ class SignificanceResultTest {
 
     @ParameterizedTest
     @ValueSource(doubles = {-1, -Double.MIN_VALUE, 0, 0.5000000000000001})
-    void testRejectThrows(double alpha) {
+    void testRejectThrowsWithBadAlpha(double alpha) {
         final SignificanceResult r = new SignificanceResult() {
             @Override
             public double getStatistic() {
@@ -54,6 +54,23 @@ class SignificanceResultTest {
         Assertions.assertThrows(IllegalArgumentException.class, () -> 
r.reject(alpha));
     }
 
+    @ParameterizedTest
+    @ValueSource(doubles = {-Double.MIN_VALUE, -0.1, -1, -2, 1.0 + 0x1.0p-52, 
1.1, -Double.MAX_VALUE,
+        Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN})
+    void testRejectThrowsWithBadPValue(double p) {
+        final SignificanceResult r = new SignificanceResult() {
+            @Override
+            public double getStatistic() {
+                return 0;
+            }
+            @Override
+            public double getPValue() {
+                return p;
+            }
+        };
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
r.reject(0.05));
+    }
+
     @ParameterizedTest
     @ValueSource(doubles = {0, Double.MIN_VALUE, 1e-6, 0.01, 0.05, 0.5})
     void testReject(double p) {
diff --git 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
index 376e1a61..37740a9f 100644
--- 
a/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
+++ 
b/commons-statistics-inference/src/test/java/org/apache/commons/statistics/inference/TTestTest.java
@@ -448,4 +448,36 @@ class TTestTest {
             new double[] {0.50953294711549812, 0.25476647355774906, 
0.74523352644225094}));
         return builder.build();
     }
+
+    /**
+     * This test asserts the current behaviour of the TTest when a mean is not 
finite.
+     * The TTest rejects non-finite samples in array arguments, but it cannot 
reject
+     * non-finite means as this can produce a valid result (t=infinity; p=0) 
because
+     * the variance can be provided as strictly positive finite.
+     * When the statistic is NaN, the p-value is NaN and the significance 
result
+     * will raise an exception when attempting to reject the null hypothesis.
+     *
+     * See STATISTICS-97; STATISTICS-98.
+     */
+    @Test
+    void testRejectWithInvalidStatistic() {
+        final TTest test = TTest.withDefaults();
+        // Single infinity in one sample can compute a valid statistic
+        // thus we cannot validate the mean is finite for the paired test.
+        final SignificanceResult r1 = test.test(Double.POSITIVE_INFINITY, 1, 2,
+                                                1, 1, 2);
+        Assertions.assertEquals(Double.POSITIVE_INFINITY, r1.getStatistic());
+        Assertions.assertEquals(0, r1.getPValue());
+        // The two samples are not the same
+        Assertions.assertTrue(() -> r1.reject(0.05), "p=0 should reject any 
non-zero significance level");
+
+        // Infinity mean in both samples cannot compute a valid statistic.
+        // This is returned and will throw if the result is used to reject a 
null hypothesis.
+        final SignificanceResult r2 = test.test(Double.POSITIVE_INFINITY, 1, 2,
+                                                Double.POSITIVE_INFINITY, 1, 
2);
+        Assertions.assertEquals(Double.NaN, r2.getStatistic());
+        Assertions.assertEquals(Double.NaN, r2.getPValue());
+        Assertions.assertThrows(IllegalArgumentException.class,
+            () -> r2.reject(0.05));
+    }
 }
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 390ba746..58768a9d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -53,6 +53,11 @@ If the output is not quite correct, check for invisible 
trailing spaces!
   </properties>
   <body>
     <release version="1.4" date="TBD" description="Adds new features (requires 
Java 8).">
+      <action dev="aherbert" type="fix" due-to="Security scan, Aex Herbert" 
issue="STATISTICS-98">
+        "SignificanceResult": Raise an exception if the p-value is invalid when
+        comparing the p-value to a significance level. Corrects silent 
rejection
+        of the null hypothesis if the computed p-value is NaN.
+      </action>
       <action dev="aherbert" type="fix" due-to="Security scan, Aex Herbert" 
issue="STATISTICS-97">
         "TTest/OneWayAnova": Checks added for arguments that invalidate the 
test
         statistic: non-finite values in input arrays; and NaN mean arguments.

Reply via email to