Skip to content

Multiply/Divide wrap the exponent on int overflow: 1e2000000000 squared returns 1e-294967296 instead of throwing #109

Description

@matt-edmondson

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions