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-numbers.git
commit be86bfa9623031ee5edd425508bdac5003399657 Author: Alex Herbert <[email protected]> AuthorDate: Wed Oct 21 15:49:37 2026 +0100 NUMBERS-219: Correct digamma/trigamma values for infinite arguments --- .../java/org/apache/commons/numbers/gamma/Digamma.java | 15 ++++++++++++--- .../java/org/apache/commons/numbers/gamma/Trigamma.java | 11 ++++++++++- .../org/apache/commons/numbers/gamma/DigammaTest.java | 7 ++++--- .../org/apache/commons/numbers/gamma/TrigammaTest.java | 5 +++-- src/changes/changes.xml | 6 +++++- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Digamma.java b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Digamma.java index d80e5765..72321939 100644 --- a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Digamma.java +++ b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Digamma.java @@ -49,13 +49,20 @@ public final class Digamma { /** * Computes the digamma function. * - * This is an independently written implementation of the algorithm described in + * <p>Special cases: + * <ul> + * <li>If the argument is infinity, then the result is infinity.</li> + * <li>If the argument is negative infinity, then the result is nan.</li> + * <li>If the argument is nan, then the result is nan.</li> + * </ul> + * + * <p>This is an independently written implementation of the algorithm described in * <a href="https://www.uv.es/~bernardo/1976AppStatist.pdf">Jose Bernardo, * Algorithm AS 103: Psi (Digamma) Function, Applied Statistics, 1976</a>. * A <a href="https://en.wikipedia.org/wiki/Digamma_function#Reflection_formula"> * reflection formula</a> is incorporated to improve performance on negative values. * - * Some of the constants have been changed to increase accuracy at the moderate + * <p>Some of the constants have been changed to increase accuracy at the moderate * expense of run-time. The result should be accurate to within {@code 1e-8}. * relative tolerance for {@code 0 < x < 1e-5} and within {@code 1e-8} absolute * tolerance otherwise. @@ -66,7 +73,9 @@ public final class Digamma { */ public static double value(double x) { if (!Double.isFinite(x)) { - return x; + // Function oscillates between infinite poles at integer values for + // large negative x; the -infinity limit is undefined + return x == Double.POSITIVE_INFINITY ? x : Double.NaN; } double digamma = 0; diff --git a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Trigamma.java b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Trigamma.java index 8ab1dd4b..0955fc4a 100644 --- a/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Trigamma.java +++ b/commons-numbers-gamma/src/main/java/org/apache/commons/numbers/gamma/Trigamma.java @@ -46,12 +46,21 @@ public final class Trigamma { /** * Computes the trigamma function. * + * <p>Special cases: + * <ul> + * <li>If the argument is infinity, then the result is 0.</li> + * <li>If the argument is negative infinity, then the result is nan.</li> + * <li>If the argument is nan, then the result is nan.</li> + * </ul> + * * @param x Argument. * @return trigamma(x) to within {@code 1e-8} relative or absolute error whichever is larger. */ public static double value(double x) { if (!Double.isFinite(x)) { - return x; + // Function oscillates between infinite poles at integer values for + // large negative x; the -infinity limit is undefined + return x == Double.POSITIVE_INFINITY ? 0 : Double.NaN; } if (x < 0) { diff --git a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/DigammaTest.java b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/DigammaTest.java index a83261d2..92d439e2 100644 --- a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/DigammaTest.java +++ b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/DigammaTest.java @@ -68,9 +68,10 @@ class DigammaTest { @Test void testDigammaNonRealArgs() { - Assertions.assertTrue(Double.isNaN(Digamma.value(Double.NaN))); - Assertions.assertTrue(Double.isInfinite(Digamma.value(Double.POSITIVE_INFINITY))); - Assertions.assertTrue(Double.isInfinite(Digamma.value(Double.NEGATIVE_INFINITY))); + // See NUMBERS-219 + Assertions.assertEquals(Double.NaN, Digamma.value(Double.NaN)); + Assertions.assertEquals(Double.POSITIVE_INFINITY, Digamma.value(Double.POSITIVE_INFINITY)); + Assertions.assertEquals(Double.NaN, Digamma.value(Double.NEGATIVE_INFINITY)); } private void checkRelativeError(String msg, diff --git a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TrigammaTest.java b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TrigammaTest.java index 22ceae6e..938143db 100644 --- a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TrigammaTest.java +++ b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/TrigammaTest.java @@ -110,9 +110,10 @@ class TrigammaTest { @Test void testTrigammaNonRealArgs() { + // See NUMBERS-219 Assertions.assertEquals(Double.NaN, Trigamma.value(Double.NaN)); - Assertions.assertEquals(Double.POSITIVE_INFINITY, Trigamma.value(Double.POSITIVE_INFINITY)); - Assertions.assertEquals(Double.NEGATIVE_INFINITY, Trigamma.value(Double.NEGATIVE_INFINITY)); + Assertions.assertEquals(0, Trigamma.value(Double.POSITIVE_INFINITY)); + Assertions.assertEquals(Double.NaN, Trigamma.value(Double.NEGATIVE_INFINITY)); } } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 704ab630..59b2a940 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -56,7 +56,11 @@ If the output is not quite correct, check for invisible trailing spaces! <release version="1.4" date="TBD" description=" New features, updates and bug fixes. "> - <action type="fix" due-to="Aryan Ganjoo" issue="NUMBERS-218"> + <action dev="aherbert" type="fix" due-to="Alex Herbert" issue="NUMBERS-219"> + "Digamma/Trigamma": Value at -infinity is undefined (NaN). Value at +infinity + is asymptotically +infinity (digamma) and 0 (trigamma). + </action> + <action dev="aherbert" type="fix" due-to="Aryan Ganjoo" issue="NUMBERS-218"> "ArithmeticUtils.pow": Return the representable minimum int and long values for (-2)^31 and (-2)^63 instead of reporting overflow. </action>
