NUMBERS-150: Fix Fraction.pow and BigFraction.pow
Modified to:
- correctly handle Integer.MIN_VALUE as the argument.
- throw an ArithmeticException when zero is raised to a negative power.
Squashed commit of the following:
commit 0fd1da5bdb92bf31b7dd4ec3981924c5f68dbef4
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 04:36:46 2020 +0800
fix checkstyles
commit 4c2aa76d0f4425e55eccd23e4a40a19eb9834a31
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 04:34:54 2020 +0800
apply suggestions
commit 0fc3a52c4d3789b18795392223e6add522843a0a
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 04:27:42 2020 +0800
apply suggestions
commit 1481998f24c49bf8f20123703037a6fbefe6b9af
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 04:24:58 2020 +0800
apply suggestions
commit 44678423889201f2c60ac024e132593336033bc9
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:28:58 2020 +0800
fix checkstyles
commit 708414cdb8046c442360469f294b77729f5d345c
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:27:43 2020 +0800
change as suggested
commit 5b9c91aaae49f7b2d8d331b2d8947bd1b684151e
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:24:56 2020 +0800
change as suggested
commit 12142b608c3776e498fd17d66d51f4a7bc48e8f8
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:23:57 2020 +0800
change as suggested
commit 3f10f12e3f9bb79edb835682cefeeea5ccf6a7ca
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:22:13 2020 +0800
change as suggested
commit d01b5b8f509c5c9641798d57e4de1434cbac9688
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:20:50 2020 +0800
change as suggested
commit 6f23df0d9fe789faff07406482108810b0eaf8b4
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:16:33 2020 +0800
change as suggested
commit 3646e00ce9c17a81ca3a64ffe103974c44b33995
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:15:10 2020 +0800
change as suggested
commit 3d4bc49f704ee0784ccdc96ab61edeb3720dbff3
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:13:29 2020 +0800
change as suggested
commit 267df9448e14d28451e9d2ad88783f4bc32b635b
Author: XenoAmess <xenoamess@gmail.com>
Date: Wed Aug 26 03:12:04 2020 +0800
change as suggested
commit ba0d39ba532f36e62417b277641932fe46f6af23
Author: XenoAmess <xenoamess@gmail.com>
Date: Tue Aug 25 21:26:27 2020 +0800
[NUMBERS-150] fix bug in Fraction.pow and BigFraction.pow
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 a172d44..bc71783 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
@@ -966,23 +966,36 @@
*/
@Override
public BigFraction pow(final int exponent) {
+ if (exponent == 1) {
+ return this;
+ }
if (exponent == 0) {
return ONE;
}
if (isZero()) {
+ if (exponent < 0) {
+ throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+ }
return ZERO;
}
-
+ if (exponent > 0) {
+ return new BigFraction(numerator.pow(exponent),
+ denominator.pow(exponent));
+ }
+ if (exponent == -1) {
+ return this.reciprocal();
+ }
+ if (exponent == Integer.MIN_VALUE) {
+ // MIN_VALUE can't be negated
+ return new BigFraction(denominator.pow(Integer.MAX_VALUE).multiply(denominator),
+ numerator.pow(Integer.MAX_VALUE).multiply(numerator));
+ }
// Note: Raise the BigIntegers to the power and then reduce.
// The supported range for BigInteger is currently
// +/-2^(Integer.MAX_VALUE) exclusive thus larger
// exponents (long, BigInteger) are currently not supported.
- if (exponent < 0) {
- return new BigFraction(denominator.pow(-exponent),
- numerator.pow(-exponent));
- }
- return new BigFraction(numerator.pow(exponent),
- denominator.pow(exponent));
+ return new BigFraction(denominator.pow(-exponent),
+ numerator.pow(-exponent));
}
/**
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
index f1bc542..2afe738 100644
--- a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
@@ -775,19 +775,32 @@
*/
@Override
public Fraction pow(final int exponent) {
+ if (exponent == 1) {
+ return this;
+ }
if (exponent == 0) {
return ONE;
}
if (isZero()) {
+ if (exponent < 0) {
+ throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+ }
return ZERO;
}
-
- if (exponent < 0) {
- return new Fraction(ArithmeticUtils.pow(denominator, -exponent),
- ArithmeticUtils.pow(numerator, -exponent));
+ if (exponent > 0) {
+ return new Fraction(ArithmeticUtils.pow(numerator, exponent),
+ ArithmeticUtils.pow(denominator, exponent));
}
- return new Fraction(ArithmeticUtils.pow(numerator, exponent),
- ArithmeticUtils.pow(denominator, exponent));
+ if (exponent == -1) {
+ return this.reciprocal();
+ }
+ if (exponent == Integer.MIN_VALUE) {
+ // MIN_VALUE can't be negated
+ return new Fraction(ArithmeticUtils.pow(denominator, Integer.MAX_VALUE) * denominator,
+ ArithmeticUtils.pow(numerator, Integer.MAX_VALUE) * numerator);
+ }
+ return new Fraction(ArithmeticUtils.pow(denominator, -exponent),
+ ArithmeticUtils.pow(numerator, -exponent));
}
/**
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
index 5ded68e..f6c147f 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
@@ -778,4 +778,15 @@
fractionA.getDenominator().multiply(fractionB.getDenominator()));
Assertions.assertEquals(correctResult, errorResult);
}
+
+ @Test
+ void testNumbers150() {
+ // zero to negative powers should throw an exception
+ Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.ZERO.pow(-1));
+ Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.ZERO.pow(Integer.MIN_VALUE));
+
+ // shall overflow
+ Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.of(2).pow(Integer.MIN_VALUE));
+ Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.of(1, 2).pow(Integer.MIN_VALUE));
+ }
}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
index d6e58dc..621d02a 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
@@ -570,6 +570,10 @@
testCases.add(new BinaryIntOperatorTestCase(0, 1, Integer.MAX_VALUE, 0, 1));
testCases.add(new BinaryIntOperatorTestCase(0, -1, Integer.MAX_VALUE, 0, 1));
+ testCases.add(new BinaryIntOperatorTestCase(1, 1, Integer.MIN_VALUE, 1, 1));
+ testCases.add(new BinaryIntOperatorTestCase(1, -1, Integer.MIN_VALUE, 1, 1));
+ testCases.add(new BinaryIntOperatorTestCase(-1, 1, Integer.MIN_VALUE, 1, 1));
+ testCases.add(new BinaryIntOperatorTestCase(-1, -1, Integer.MIN_VALUE, 1, 1));
return testCases;
}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
index 1a2a1b6..8b5506d 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
@@ -622,6 +622,17 @@
assertFraction(1, Integer.MAX_VALUE, b.divide(2));
}
+ @Test
+ void testNumbers150() {
+ // zero to negative powers should throw an exception
+ Assertions.assertThrows(ArithmeticException.class, () -> Fraction.ZERO.pow(-1));
+ Assertions.assertThrows(ArithmeticException.class, () -> Fraction.ZERO.pow(Integer.MIN_VALUE));
+
+ // shall overflow
+ Assertions.assertThrows(ArithmeticException.class, () -> Fraction.of(2).pow(Integer.MIN_VALUE));
+ Assertions.assertThrows(ArithmeticException.class, () -> Fraction.of(1, 2).pow(Integer.MIN_VALUE));
+ }
+
/**
* Defines test cases that cause overflow in {@link Fraction#add(Fraction)}.
* @return a list of test cases