What's wrong
Multiply and Divide compute the result exponent with unchecked int arithmetic:
Multiply (PreciseNumber/PreciseNumber.cs ~L1429): new PreciseNumber(left.Exponent + right.Exponent, ...)
Divide (~L1487): int exponent = left.Exponent - right.Exponent;
- The same pattern appears in
TryDivideExactly and DivideToPrecision (~L1546, L1565, L1586)
Other parts of the library already guard against this overflow. The constructor uses checked(exponent + trailingZeros), Parse uses checked(exponent - decimalDigits), and ToString widens to long. Pow also documents OverflowException "when the result needs an exponent outside the range of an int". But integer Pow goes through Multiply, so it wraps too, and that documented exception never happens.
Failure scenario (reproduced)
With big = Parse("1e2000000000") and small = Parse("1e-2000000000"):
| Expression |
Result |
big * big |
1e-294967296 |
small * small |
1e+294967296 |
big / small |
1e-294967296 |
(big * big) > big |
false |
PreciseNumber.Pow(big, 2) |
1e-294967296 (the documented OverflowException is not thrown) |
The result is a silently wrong value, with its magnitude off by billions of orders. An arbitrary-precision type exists to avoid exactly this kind of error.
Suggested fix
Compute the exponent in checked(...), or in long with an explicit range check. Either way, throw OverflowException, the same way the constructor and Parse already do.
Acceptance criteria
big * big, small * small, big / small, and Pow(big, 2) all throw OverflowException.
- Tests cover
Multiply, Divide, and the precision-division paths at the int exponent boundary.
What's wrong
MultiplyandDividecompute the result exponent with uncheckedintarithmetic:Multiply(PreciseNumber/PreciseNumber.cs~L1429):new PreciseNumber(left.Exponent + right.Exponent, ...)Divide(~L1487):int exponent = left.Exponent - right.Exponent;TryDivideExactlyandDivideToPrecision(~L1546, L1565, L1586)Other parts of the library already guard against this overflow. The constructor uses
checked(exponent + trailingZeros),Parseuseschecked(exponent - decimalDigits), andToStringwidens tolong.Powalso documentsOverflowException"when the result needs an exponent outside the range of an int". But integerPowgoes throughMultiply, so it wraps too, and that documented exception never happens.Failure scenario (reproduced)
With
big = Parse("1e2000000000")andsmall = Parse("1e-2000000000"):big * big1e-294967296small * small1e+294967296big / small1e-294967296(big * big) > bigPreciseNumber.Pow(big, 2)1e-294967296(the documentedOverflowExceptionis not thrown)The result is a silently wrong value, with its magnitude off by billions of orders. An arbitrary-precision type exists to avoid exactly this kind of error.
Suggested fix
Compute the exponent in
checked(...), or inlongwith an explicit range check. Either way, throwOverflowException, the same way the constructor andParsealready do.Acceptance criteria
big * big,small * small,big / small, andPow(big, 2)all throwOverflowException.Multiply,Divide, and the precision-division paths at theintexponent boundary.