diff --git a/src/main/java/org/apache/commons/lang3/math/Fraction.java b/src/main/java/org/apache/commons/lang3/math/Fraction.java index a2a45ac83f2..5864a5d7496 100644 --- a/src/main/java/org/apache/commons/lang3/math/Fraction.java +++ b/src/main/java/org/apache/commons/lang3/math/Fraction.java @@ -174,7 +174,10 @@ public static Fraction getFraction(double value) { if (i == 25) { throw new ArithmeticException("Unable to convert double to fraction"); } - return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0); + // wholeNumber can be up to Integer.MAX_VALUE while denom0 > 1 for any non-integer value, + // so the int product overflows for values near the limit; check it instead of wrapping silently. + final int numerator = Math.addExact(numer0, mulAndCheck(wholeNumber, denom0)); + return getReducedFraction(numerator * sign, denom0); } /** diff --git a/src/test/java/org/apache/commons/lang3/math/FractionTest.java b/src/test/java/org/apache/commons/lang3/math/FractionTest.java index 862e383b1d8..f068402c78f 100644 --- a/src/test/java/org/apache/commons/lang3/math/FractionTest.java +++ b/src/test/java/org/apache/commons/lang3/math/FractionTest.java @@ -334,6 +334,10 @@ void testFactory_double() { assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Double.POSITIVE_INFINITY)); assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Double.NEGATIVE_INFINITY)); assertThrows(ArithmeticException.class, () -> Fraction.getFraction((double) Integer.MAX_VALUE + 1)); + // near Integer.MAX_VALUE with a fractional part: numerator overflows an int, so it must throw + // rather than silently return a wrong fraction (previously -3/2 and -2147483647/2 respectively) + assertThrows(ArithmeticException.class, () -> Fraction.getFraction(2147483646.5d)); + assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1073741824.5d)); // zero Fraction f = Fraction.getFraction(0.0d);