diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java index 7c8ce9c7a..a67c76511 100644 --- a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java +++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java @@ -20,6 +20,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.util.Iterator; import org.apache.commons.numbers.core.ArithmeticUtils; /** @@ -88,108 +89,6 @@ private BigFraction(BigInteger num, BigInteger den) { } } - /** - * Create a fraction given the double value and either the maximum - * error allowed or the maximum number of denominator digits. - * - *

- * NOTE: This method is called with - * - EITHER a valid epsilon value and the maxDenominator set to - * Integer.MAX_VALUE (that way the maxDenominator has no effect) - * - OR a valid maxDenominator value and the epsilon value set to - * zero (that way epsilon only has effect if there is an exact - * match before the maxDenominator value is reached). - *

- *

- * It has been done this way so that the same code can be reused for - * both scenarios. However this could be confusing to users if it - * were part of the public API and this method should therefore remain - * PRIVATE. - *

- * - * See JIRA issue ticket MATH-181 for more details: - * https://issues.apache.org/jira/browse/MATH-181 - * - * @param value Value to convert to a fraction. - * @param epsilon Maximum error allowed. - * The resulting fraction is within {@code epsilon} of {@code value}, - * in absolute terms. - * @param maxDenominator Maximum denominator value allowed. - * @param maxIterations Maximum number of convergents. - * @throws ArithmeticException if the continued fraction failed to converge. - */ - private static BigFraction from(final double value, - final double epsilon, - final int maxDenominator, - final int maxIterations) { - long overflow = Integer.MAX_VALUE; - double r0 = value; - long a0 = (long) Math.floor(r0); - - if (Math.abs(a0) > overflow) { - throw new FractionException(FractionException.ERROR_CONVERSION_OVERFLOW, value, a0, 1l); - } - - // check for (almost) integer arguments, which should not go - // to iterations. - if (Math.abs(a0 - value) < epsilon) { - return new BigFraction(BigInteger.valueOf(a0), - BigInteger.ONE); - } - - long p0 = 1; - long q0 = 0; - long p1 = a0; - long q1 = 1; - - long p2 = 0; - long q2 = 1; - - int n = 0; - boolean stop = false; - do { - ++n; - final double r1 = 1d / (r0 - a0); - final long a1 = (long) Math.floor(r1); - p2 = (a1 * p1) + p0; - q2 = (a1 * q1) + q0; - if (p2 > overflow || - q2 > overflow) { - // in maxDenominator mode, if the last fraction was very close to the actual value - // q2 may overflow in the next iteration; in this case return the last one. - if (epsilon == 0 && - Math.abs(q1) < maxDenominator) { - break; - } - throw new FractionException(FractionException.ERROR_CONVERSION_OVERFLOW, value, p2, q2); - } - - final double convergent = (double) p2 / (double) q2; - if (n < maxIterations && - Math.abs(convergent - value) > epsilon && - q2 < maxDenominator) { - p0 = p1; - p1 = p2; - q0 = q1; - q1 = q2; - a0 = a1; - r0 = r1; - } else { - stop = true; - } - } while (!stop); - - if (n >= maxIterations) { - throw new FractionException(FractionException.ERROR_CONVERSION, value, maxIterations); - } - - return q2 < maxDenominator ? - new BigFraction(BigInteger.valueOf(p2), - BigInteger.valueOf(q2)) : - new BigFraction(BigInteger.valueOf(p1), - BigInteger.valueOf(q1)); - } - /** *

* Create a {@link BigFraction} equivalent to the passed {@code BigInteger}, ie @@ -288,6 +187,14 @@ public static BigFraction from(final double value) { /** * Create a fraction given the double value and maximum error allowed. *

+ * This factory method approximates the given {@code double} value with a + * fraction such that no other fraction within the given interval will have + * a smaller or equal denominator, unless {@code |epsilon| > 0.5}, in which + * case the integer closest to the value to be approximated will be returned + * (if there are two equally distant integers within the specified interval, + * either of them will be returned). + *

+ *

* References: *