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..a3da4fd 100644 --- a/PreciseNumber/PreciseNumber.cs +++ b/PreciseNumber/PreciseNumber.cs @@ -1908,24 +1908,7 @@ public PreciseNumber Pow(PreciseNumber power) if (IsInteger(power)) { - // 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 @@ -1942,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. ///