Skip to content
Open
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
18 changes: 18 additions & 0 deletions src/Numerics.Tests/PrecisionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,24 @@ public void IncrementDecrementWithCount()
Assert.AreEqual(-2 * double.Epsilon, x);
}

/// <summary>
/// Increment/Decrement with a count of int.MinValue must not overflow the stack.
/// </summary>
[Test]
public void IncrementDecrementWithIntMinValueCount()
{
// Regression: Increment/Decrement(value, int.MinValue) used to throw a StackOverflowException
// because -int.MinValue overflows back to int.MinValue, so Increment <-> Decrement recursed
// forever. A count of int.MinValue must step |int.MinValue| (2147483648) representable values
// in the opposite direction, like any other negative count.
const long magnitude = -(long)int.MinValue; // 2147483648
long bits = BitConverter.DoubleToInt64Bits(1.0);

var x = 1.0;
Assert.AreEqual(BitConverter.Int64BitsToDouble(bits - magnitude), x.Increment(int.MinValue));
Assert.AreEqual(BitConverter.Int64BitsToDouble(bits + magnitude), x.Decrement(int.MinValue));
}

/// <summary>
/// Increment at min/max.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/Numerics/Precision.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@ static int AsDirectionalInt32(float value)
/// </remarks>
/// <returns>The next larger floating point value.</returns>
public static double Increment(this double value, int count = 1)
{
return Increment(value, (long)count);
}

// The real logic lives in the long-count overloads. A negative count delegates to the sibling
// with the negated count; performing the negation in long means a count of int.MinValue -- whose
// int negation overflows back to int.MinValue and would make Increment/Decrement recurse forever
// into a StackOverflowException -- is handled like any other negative count
// (-(long)int.MinValue == 2147483648).
static double Increment(double value, long count)
{
if (double.IsInfinity(value) || double.IsNaN(value) || count == 0)
{
Expand Down Expand Up @@ -311,6 +321,11 @@ public static double Increment(this double value, int count = 1)
/// </remarks>
/// <returns>The next smaller floating point value.</returns>
public static double Decrement(this double value, int count = 1)
{
return Decrement(value, (long)count);
}

static double Decrement(double value, long count)
{
if (double.IsInfinity(value) || double.IsNaN(value) || count == 0)
{
Expand Down