From 53f55d2ca07cd243f0961e6a984180637a0787cc Mon Sep 17 00:00:00 2001 From: Gilles Sadowski Date: Tue, 29 Oct 2013 12:01:12 +0000 Subject: [PATCH] MATH-1047 Overflow checking in "ArithmeticUtils.pow(int,int)". Added "LocalizedFormats.BASE" error pattern. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1536683 13f79535-47bb-0310-9956-ffa450edef68 --- .../exception/util/LocalizedFormats.java | 1 + .../commons/math3/util/ArithmeticUtils.java | 44 ++++++++++++++----- .../util/LocalizedFormats_fr.properties | 1 + .../exception/util/LocalizedFormatsTest.java | 2 +- .../math3/util/ArithmeticUtilsTest.java | 44 +++++++++++++++++++ 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java index eda413d96..854f153e0 100644 --- a/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java +++ b/src/main/java/org/apache/commons/math3/exception/util/LocalizedFormats.java @@ -205,6 +205,7 @@ public enum LocalizedFormats implements Localizable { NOT_POSITIVE_ELEMENT_AT_INDEX("element {0} is not positive: {1}"), NOT_POSITIVE_EXPONENT("invalid exponent {0} (must be positive)"), NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE("number of elements should be positive ({0})"), + BASE("base ({0})"), /* keep */ EXPONENT("exponent ({0})"), /* keep */ NOT_POSITIVE_LENGTH("length must be positive ({0})"), LENGTH("length ({0})"), /* keep */ diff --git a/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java b/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java index 87b9d8de0..eb474a3b7 100644 --- a/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java +++ b/src/main/java/org/apache/commons/math3/util/ArithmeticUtils.java @@ -632,25 +632,45 @@ public final class ArithmeticUtils { * * @param k Number to raise. * @param e Exponent (must be positive or zero). - * @return ke + * @return \( k^e \) * @throws NotPositiveException if {@code e < 0}. + * @throws MathArithmeticException if the result would overflow. */ - public static int pow(final int k, int e) throws NotPositiveException { + public static int pow(final int k, + final int e) + throws NotPositiveException, + MathArithmeticException { if (e < 0) { throw new NotPositiveException(LocalizedFormats.EXPONENT, e); } - int result = 1; - int k2p = k; - while (e != 0) { - if ((e & 0x1) != 0) { - result *= k2p; - } - k2p *= k2p; - e = e >> 1; - } + try { + int exp = e; + int result = 1; + int k2p = k; + while (true) { + if ((exp & 0x1) != 0) { + result = mulAndCheck(result, k2p); + } - return result; + exp >>= 1; + if (exp == 0) { + break; + } + + k2p = mulAndCheck(k2p, k2p); + } + + return result; + } catch (MathArithmeticException mae) { + // Add context information. + mae.getContext().addMessage(LocalizedFormats.OVERFLOW); + mae.getContext().addMessage(LocalizedFormats.BASE, k); + mae.getContext().addMessage(LocalizedFormats.EXPONENT, e); + + // Rethrow. + throw mae; + } } /** diff --git a/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties b/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties index 3658b7c23..45cdba3bc 100644 --- a/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties +++ b/src/main/resources/assets/org/apache/commons/math3/exception/util/LocalizedFormats_fr.properties @@ -176,6 +176,7 @@ DEGREES_OF_FREEDOM = degr\u00e9s de libert\u00e9 ({0}) NOT_POSITIVE_ELEMENT_AT_INDEX = l''\u00e9l\u00e9ment {0} n''est pas positif : {1} NOT_POSITIVE_EXPONENT = exposant {0} invalide (doit \u00eatre positif) NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE = le nombre d''\u00e9l\u00e9ments devrait \u00eatre positif ({0}) +BASE = base ({0}) EXPONENT = exposant ({0}) NOT_POSITIVE_LENGTH = la longueur doit \u00eatre positive ({0}) LENGTH = longueur ({0}) diff --git a/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java b/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java index ac052eb08..313210a29 100644 --- a/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java +++ b/src/test/java/org/apache/commons/math3/exception/util/LocalizedFormatsTest.java @@ -30,7 +30,7 @@ public class LocalizedFormatsTest { @Test public void testMessageNumber() { - Assert.assertEquals(314, LocalizedFormats.values().length); + Assert.assertEquals(315, LocalizedFormats.values().length); } @Test diff --git a/src/test/java/org/apache/commons/math3/util/ArithmeticUtilsTest.java b/src/test/java/org/apache/commons/math3/util/ArithmeticUtilsTest.java index 3de2fae3b..b01fe5041 100644 --- a/src/test/java/org/apache/commons/math3/util/ArithmeticUtilsTest.java +++ b/src/test/java/org/apache/commons/math3/util/ArithmeticUtilsTest.java @@ -460,6 +460,50 @@ public class ArithmeticUtilsTest { } + @Test(expected=MathArithmeticException.class) + public void testPowIntIntOverflow() { + final int base = 21; + final int exp = 8; + ArithmeticUtils.pow(base, exp); + } + @Test + public void testPowIntIntNoOverflow() { + final int base = 21; + final int exp = 7; + ArithmeticUtils.pow(base, exp); + } + + @Test(expected=MathArithmeticException.class) + public void testPowNegativeIntIntOverflow() { + final int base = -21; + final int exp = 8; + ArithmeticUtils.pow(base, exp); + } + @Test + public void testPowNegativeIntIntNoOverflow() { + final int base = -21; + final int exp = 7; + ArithmeticUtils.pow(base, exp); + } + + @Test + public void testPowMinusOneInt() { + final int base = -1; + for (int i = 0; i < 100; i++) { + final int pow = ArithmeticUtils.pow(base, i); + Assert.assertEquals("i: " + i, i % 2 == 0 ? 1 : -1, pow); + } + } + + @Test + public void testPowOneInt() { + final int base = 1; + for (int i = 0; i < 100; i++) { + final int pow = ArithmeticUtils.pow(base, i); + Assert.assertEquals("i: " + i, 1, pow); + } + } + @Test public void testIsPowerOfTwo() { final int n = 1025;