Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions PreciseNumber.Test/PreciseNumberTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
54 changes: 36 additions & 18 deletions PreciseNumber/PreciseNumber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@
/// A fixed precision such as <c>E15</c> rounds values that need 17 digits, which turns
/// <see cref="double.MaxValue"/> into a number that converts back to infinity.
/// </remarks>
internal static string GetStringFormatForFloatType<TFloat>()

Check warning on line 734 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Remove this method and declare a constant for this value.

Check warning on line 734 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Remove this method and declare a constant for this value.
where TFloat : INumber<TFloat>
=> "R";

Expand Down Expand Up @@ -1189,7 +1189,7 @@
public static PreciseNumber MinMagnitudeNumber(PreciseNumber x, PreciseNumber y) => MinMagnitude(x, y);

/// <inheritdoc/>
public static PreciseNumber Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)

Check warning on line 1192 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 1192 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Refactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.

Check warning on line 1192 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

All 'Parse' method overloads should be adjacent.

Check warning on line 1192 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

All 'Parse' method overloads should be adjacent.
{
// Read the separator and signs from the same culture TryFormat writes them with, so that
// Parse(x.ToString(p), p) round-trips for every provider.
Expand Down Expand Up @@ -1237,14 +1237,14 @@
}

hasDecimal = true;
i += decimalSeparator.Length - 1;

Check warning on line 1240 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Do not update the stop condition variable 'i' in the body of the for loop.

Check warning on line 1240 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Do not update the stop condition variable 'i' in the body of the for loop.
continue;
}

// Group separators may only sit between integral digits.
if (allowThousands && !hasDecimal && digitCount > 0 && s[i..].StartsWith(groupSeparator, StringComparison.Ordinal))
{
i += groupSeparator.Length - 1;

Check warning on line 1247 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Do not update the stop condition variable 'i' in the body of the for loop.

Check warning on line 1247 in PreciseNumber/PreciseNumber.cs

View workflow job for this annotation

GitHub Actions / ci / .NET / Analyze & Release

Do not update the stop condition variable 'i' in the body of the for loop.
continue;
}

Expand Down Expand Up @@ -1908,24 +1908,7 @@

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<int>(); 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
Expand All @@ -1942,6 +1925,41 @@
return FractionalPow(this, power, significantDigits);
}

/// <summary>
/// Returns the current number raised to an integer power, exactly.
/// </summary>
/// <param name="power">The integer power, which is not zero.</param>
/// <returns>The current number raised to <paramref name="power"/>.</returns>
/// <exception cref="OverflowException">Thrown when the result needs an exponent outside the range of an <see cref="int"/>.</exception>
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<int>(); remaining > 0; remaining >>= 1)
{
if ((remaining & 1) != 0)
{
result *= factor;
}

if (remaining > 1)
{
factor = factor.Squared();
}
}

return power.Significand.Sign < 0 ? One / result : result;
}

/// <summary>
/// Returns zero raised to a non-zero power.
/// </summary>
Expand Down
Loading