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


The following commit(s) were added to refs/heads/master by this push:
     new f5d0de68 NUMBERS-214: Lower default iterations
f5d0de68 is described below

commit f5d0de6892bc701bd75beff80e6d9c374190fc09
Author: Alex Herbert <[email protected]>
AuthorDate: Fri Aug 21 10:05:26 2026 +0100

    NUMBERS-214: Lower default iterations
---
 .../fraction/GeneralizedContinuedFraction.java     | 12 ++++++++--
 .../fraction/GeneralizedContinuedFractionTest.java | 26 ++++++++++++++++++++++
 src/changes/changes.xml                            |  5 +++++
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git 
a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.java
 
b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.java
index 0b5a0882..c3c3ee36 100644
--- 
a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.java
+++ 
b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.java
@@ -61,8 +61,16 @@ public final class GeneralizedContinuedFraction {
      * eps * |b_n|, e.g., 1e-50".
      */
     static final double SMALL = 1e-50;
-    /** Default maximum number of iterations. */
-    static final int DEFAULT_ITERATIONS = Integer.MAX_VALUE;
+    /**
+     * Default maximum number of iterations.
+     *
+     * <p>This bounds the cost of evaluating a fraction that neither converges 
nor
+     * diverges when using the methods that do not accept a {@code 
maxIterations}
+     * argument. The default is sufficient to converge typical fractions to
+     * {@code double} precision. Slowly converging fractions may be evaluated 
using
+     * the methods that accept a {@code maxIterations} argument to raise the 
limit.
+     */
+    static final int DEFAULT_ITERATIONS = 1_000_000;
     /**
      * Minimum relative error epsilon. Equal to 1 - Math.nextDown(1.0), or 
2^-53.
      *
diff --git 
a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
 
b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
index 7eb68237..d503b32c 100644
--- 
a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
+++ 
b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
@@ -240,6 +240,32 @@ class GeneralizedContinuedFractionTest {
         assertExceptionMessageContains(t, "max");
     }
 
+    /**
+     * Evaluate a fraction that neither converges nor diverges using the 
default
+     * iteration limit. The divergent form K(1/0) oscillates: the update deltaN
+     * alternates between approximately 1e50 and 1e-50 so the evaluation never
+     * meets the convergence, divergence or zero-update exit conditions. The
+     * default iteration limit must bound the work performed before the maximum
+     * iterations exception is raised.
+     */
+    @Test
+    void testDefaultIterationsBoundsNonConvergingFraction() {
+        final int[] calls = {0};
+        final Supplier<Coefficient> gen = () -> {
+            calls[0]++;
+            // The first term provides b0 to seed the evaluation (a is 
discarded).
+            // All subsequent terms (a=1, b=0) create a non-converging 
oscillation.
+            return Coefficient.of(1, calls[0] == 1 ? 1 : 0);
+        };
+        final Throwable t = Assertions.assertThrows(ArithmeticException.class,
+            () -> GeneralizedContinuedFraction.value(gen));
+        assertExceptionMessageContains(t, "max");
+        // 1 call to seed b0 + 1 call per iteration
+        Assertions.assertTrue(GeneralizedContinuedFraction.DEFAULT_ITERATIONS 
<= 1_000_000,
+            "Default iterations should bound worst-case evaluation cost");
+        Assertions.assertEquals(1 + 
GeneralizedContinuedFraction.DEFAULT_ITERATIONS, calls[0]);
+    }
+
     @Test
     void testNaNThrowsA() {
         // Create a NaN during the iteration
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 4aabcfc4..f30c55f8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -56,6 +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 dev="aherbert" type="update" due-to="Security scan, Alex 
Herbert" issue="NUMBERS-214">
+        "GeneralizedContinuedFraction": Lower the defaut iterations to 
1_000_000. This
+        introduces a behavioural change for slowly converging fractions that 
may now
+        raise an ArithemticException due to non-convergence within the 
iteration limit.
+      </action>
       <action dev="aherbert" type="add" due-to="Alex Herbert" 
issue="NUMBERS-217">
         "BigFraction": Allow conversion to BigDecimal using a MathContext.
       </action>

Reply via email to