From 22f8848ee0958c5154e859ece5b62794b46aac3e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 14:31:05 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Return=20=C2=B11=20for=20-1=20raised=20to?= =?UTF-8?q?=20an=20integer=20too=20large=20for=20an=20int=20[patch]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The integer-power loop counts the exponent down as an int, so (-1)^n threw OverflowException for |n| >= 2^31 although the result is always ±1. Answer -1 by the parity of the power before the loop, as +1 already is. Fixes ktsu-dev/PreciseNumber#128 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JGKBe8wvU3P6jYj9MkNC69 --- PreciseNumber.Test/PreciseNumberTests.cs | 16 ++++++++++++++++ PreciseNumber/PreciseNumber.cs | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/PreciseNumber.Test/PreciseNumberTests.cs b/PreciseNumber.Test/PreciseNumberTests.cs index 5683130..70afcac 100644 --- a/PreciseNumber.Test/PreciseNumberTests.cs +++ b/PreciseNumber.Test/PreciseNumberTests.cs @@ -1879,6 +1879,22 @@ public void PowNegativePowerShouldReturnCorrectValue() Assert.AreEqual(expected, result); } + [TestMethod] + [DataRow("2147483648", 1)] + [DataRow("-2147483648", 1)] + [DataRow("1e20", 1)] + [DataRow("2147483649", -1)] + [DataRow("-2147483649", -1)] + [DataRow("2147483647", -1)] + [DataRow("2", 1)] + [DataRow("-3", -1)] + public void PowOfNegativeOneIsParityOfAnyIntegerPower(string power, int expected) + { + PreciseNumber result = PreciseNumber.NegativeOne.Pow(PreciseNumber.Parse(power, CultureInfo.InvariantCulture)); + + Assert.AreEqual(expected.ToPreciseNumber(), result, $"(-1)^{power}"); + } + [TestMethod] public void TestExpWithZeroPower() { diff --git a/PreciseNumber/PreciseNumber.cs b/PreciseNumber/PreciseNumber.cs index db7cc7f..71cecbd 100644 --- a/PreciseNumber/PreciseNumber.cs +++ b/PreciseNumber/PreciseNumber.cs @@ -1908,6 +1908,13 @@ public PreciseNumber Pow(PreciseNumber power) if (IsInteger(power)) { + // (-1)^n is ±1 by parity alone. Answering it here keeps an exponent too large for an int, which + // the loop below cannot count down, from throwing for a result that is always in range. + if (Exponent == 0 && Significand == BigInteger.MinusOne) + { + return IsEvenInteger(power) ? One : NegativeOne; + } + // Exponentiation by squaring: O(log n) multiplications instead of O(n). PreciseNumber result = One; PreciseNumber factor = this; From 3748b6f8791d2499b6fd1488cc24982728034a6f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 14:45:04 +0000 Subject: [PATCH 2/2] Move the integer-power path into IntegerPow to keep Pow's complexity down SonarCloud S3776 flagged Pow at cognitive complexity 21 (limit 15) once the (-1)^n shortcut was added. The integer branch is now its own method; behaviour is unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JGKBe8wvU3P6jYj9MkNC69 --- PreciseNumber/PreciseNumber.cs | 61 ++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/PreciseNumber/PreciseNumber.cs b/PreciseNumber/PreciseNumber.cs index 71cecbd..a3da4fd 100644 --- a/PreciseNumber/PreciseNumber.cs +++ b/PreciseNumber/PreciseNumber.cs @@ -1908,31 +1908,7 @@ public PreciseNumber Pow(PreciseNumber power) if (IsInteger(power)) { - // (-1)^n is ±1 by parity alone. Answering it here keeps an exponent too large for an int, which - // the loop below cannot count down, from throwing for a result that is always in range. - if (Exponent == 0 && Significand == BigInteger.MinusOne) - { - return IsEvenInteger(power) ? One : NegativeOne; - } - - // Exponentiation by squaring: O(log n) multiplications instead of O(n). - PreciseNumber result = One; - PreciseNumber factor = this; - - for (int remaining = power.Abs().To(); remaining > 0; remaining >>= 1) - { - if ((remaining & 1) != 0) - { - result *= factor; - } - - if (remaining > 1) - { - factor = factor.Squared(); - } - } - - return power.Significand.Sign < 0 ? One / result : result; + return IntegerPow(power); } // A fractional power is exp(y · ln x), which has no real value for a negative base. There are @@ -1949,6 +1925,41 @@ public PreciseNumber Pow(PreciseNumber power) return FractionalPow(this, power, significantDigits); } + /// + /// Returns the current number raised to an integer power, exactly. + /// + /// The integer power, which is not zero. + /// The current number raised to . + /// Thrown when the result needs an exponent outside the range of an . + private PreciseNumber IntegerPow(PreciseNumber power) + { + // (-1)^n is ±1 by parity alone. Answering it here keeps an exponent too large for an int, which + // the loop below cannot count down, from throwing for a result that is always in range. + if (Exponent == 0 && Significand == BigInteger.MinusOne) + { + return IsEvenInteger(power) ? One : NegativeOne; + } + + // Exponentiation by squaring: O(log n) multiplications instead of O(n). + PreciseNumber result = One; + PreciseNumber factor = this; + + for (int remaining = power.Abs().To(); remaining > 0; remaining >>= 1) + { + if ((remaining & 1) != 0) + { + result *= factor; + } + + if (remaining > 1) + { + factor = factor.Squared(); + } + } + + return power.Significand.Sign < 0 ? One / result : result; + } + /// /// Returns zero raised to a non-zero power. ///