What's wrong
PreciseNumber.MaxMagnitude and MinMagnitude (PreciseNumber/PreciseNumber.cs, ~lines 1100-1108) break ties in favour of the first argument:
public static PreciseNumber MaxMagnitude(PreciseNumber x, PreciseNumber y) => x.Abs() >= y.Abs() ? x : y;
public static PreciseNumber MinMagnitude(PreciseNumber x, PreciseNumber y) => x.Abs() <= y.Abs() ? x : y;
The INumberBase<T> contract, as implemented by int, double, and decimal, says what happens on a tie:
MaxMagnitude returns the positive value.
MinMagnitude returns the negative value.
MaxMagnitudeNumber and MinMagnitudeNumber delegate to these methods, so they have the same problem.
Repro
| Call |
PreciseNumber |
int / double |
MaxMagnitude(-2, 2) |
-2 |
2 |
MinMagnitude(2, -2) |
2 |
-2 |
The current tests (TestStaticMaxMagnitude and related, PreciseNumberTests.cs ~588-620) only use 1 and -1 in the order that hides the problem.
Why it matters
PreciseNumber is sold as a drop-in INumber<T>. Generic math written against INumberBase<T>, such as a norm, clamping, or picking a pivot, gives different results when T is PreciseNumber instead of double. The result also depends on argument order, so MaxMagnitude(a, b) != MaxMagnitude(b, a) for a = -b.
Suggested fix
public static PreciseNumber MaxMagnitude(PreciseNumber x, PreciseNumber y)
{
int c = x.Abs().CompareTo(y.Abs());
return c > 0 ? x : c < 0 ? y : IsNegative(x) ? y : x;
}
public static PreciseNumber MinMagnitude(PreciseNumber x, PreciseNumber y)
{
int c = x.Abs().CompareTo(y.Abs());
return c < 0 ? x : c > 0 ? y : IsNegative(x) ? x : y;
}
Acceptance criteria
MaxMagnitude(-2, 2) == 2 and MaxMagnitude(2, -2) == 2.
MinMagnitude(-2, 2) == -2 and MinMagnitude(2, -2) == -2.
- Tests cover both argument orders for the tie case, for all four methods.
What's wrong
PreciseNumber.MaxMagnitudeandMinMagnitude(PreciseNumber/PreciseNumber.cs, ~lines 1100-1108) break ties in favour of the first argument:The
INumberBase<T>contract, as implemented byint,double, anddecimal, says what happens on a tie:MaxMagnitudereturns the positive value.MinMagnitudereturns the negative value.MaxMagnitudeNumberandMinMagnitudeNumberdelegate to these methods, so they have the same problem.Repro
MaxMagnitude(-2, 2)-22MinMagnitude(2, -2)2-2The current tests (
TestStaticMaxMagnitudeand related,PreciseNumberTests.cs~588-620) only use 1 and -1 in the order that hides the problem.Why it matters
PreciseNumberis sold as a drop-inINumber<T>. Generic math written againstINumberBase<T>, such as a norm, clamping, or picking a pivot, gives different results whenTisPreciseNumberinstead ofdouble. The result also depends on argument order, soMaxMagnitude(a, b) != MaxMagnitude(b, a)fora = -b.Suggested fix
Acceptance criteria
MaxMagnitude(-2, 2) == 2andMaxMagnitude(2, -2) == 2.MinMagnitude(-2, 2) == -2andMinMagnitude(2, -2) == -2.