You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ExpM1(-1e10) throws OverflowException instead of returning -1, and ExpM1/LogP1/Sinh/Cosh/Asinh/Acosh take seconds to minutes for large arguments #133
Several functions add or subtract 1 (or 1/raised) exactly against an intermediate result whose exponent is very far from 0, and only round afterwards. CommonizeSignificands (PreciseNumber.cs, about line 923) aligns the two operands by scaling the significand by 10^|exponent gap|. That builds a BigInteger with millions of digits, and the rounding step then discards almost all of it. The call sites:
ExpM1: Subtract(Exp(x, wide), One) (PreciseNumber.Exponentials.cs, about line 489). Exp2M1 and Exp10M1 go through it.
LogP1: Log(Add(One, x)) (Exponentials.cs, about line 320). Log2P1 and Log10P1 go through it.
Sinh / Cosh: add or subtract raised and 1/raised (PreciseNumber.Hyperbolics.cs, about lines 110 and 153). The gap is about 2·|x|/ln 10.
Asinh / Acosh: Add(One, square) and Add(x, One) (Hyperbolics.cs, about lines 259 and 323-325).
Correctness:ExpM1(-1e10) throws OverflowException: A result scaled by 10^-4342944819 needs an exponent outside the range of an int. The exact answer rounds to -1 at any precision. Tanh(±1e10) already saturates to ±1 correctly, in about 30 ms. Exp2M1 and Exp10M1 throw the same way at similar magnitudes.
Performance:Exp(-1e6) takes 3 ms, while ExpM1(-1e6) takes 442 ms and returns -1. The cost grows roughly quadratically with the exponent gap:
Call
Time
Same-magnitude baseline
ExpM1(-1e7)
9.9 s, result -1
Exp(1e7): 41 ms
Exp10M1(-1e7)
45 s, result -1
ExpM1(-1e8)
over 120 s (killed)
Sinh(1e7), Cosh(-1e7)
about 43 s each, about 280 MB allocated
LogP1(1e1000000)
13 s
Log(1e1000000): 24 ms, same digits
Asinh(1e1000000)
47 s
Acosh(1e1000000)
40 s
An arbitrary-precision library will receive large or tiny arguments from user input or from generic IExponentialFunctions<T> code. Any of these calls can block a thread for minutes, or throw, where the answer is -1, Exp(x) or Log(x) to the requested precision.
Suggested fix / acceptance criteria
Short-circuit when the exponent gap exceeds significantDigits plus the guard digits. At that point the ±1 term can't affect the rounded result.
ExpM1:
Return -1 when x < -(significantDigits + guard) · ln 10.
Return Exp(x) when Exp(x) has a decimal magnitude of at least significantDigits + guard.
LogP1:
Use Log(x) when x is that large.
Return x rounded when |x| is that small. The existing small-x path may already cover this.
Sinh / Cosh: drop the 1/raised term when it is below the precision.
Asinh / Acosh: use ln(2|x|) (with sign) for large |x|.
A more general alternative: add a helper that adds two values rounded to a given precision. When one operand is below the other's last kept digit, it skips alignment and applies only the sticky or rounding effect.
Tests:
ExpM1(-1e10) returns -1 and doesn't throw.
ExpM1(-1e7), Sinh(1e7) and LogP1(1e1000000) each finish in well under a second, with the same digits as today where today's call completes.
Priority: High. ExpM1(-1e10) throws where the answer is -1. ExpM1, Exp2M1, Exp10M1, LogP1, Sinh, Cosh, Asinh and Acosh can block a thread for seconds to minutes and allocate hundreds of MB on arguments that could come from user input or generic IExponentialFunctions<T> code. That makes it a correctness bug and a denial-of-service risk.
Area / suggested owner: Transcendental functions (PreciseNumber.Exponentials.cs, PreciseNumber.Hyperbolics.cs) and exact alignment in CommonizeSignificands. This belongs to the PreciseNumber math maintainer.
Notes: The general fix, a precision-aware add that skips alignment when one operand falls below the other's last kept digit, would fix every call site at once and is probably worth doing before the per-function short-circuits. Guard the timing tests with a generous timeout so they fail fast rather than hang CI.
Activity
matt-edmondson commentedon Sep 28, 2026
Triage
ExpM1(-1e10)throws where the answer is-1.ExpM1,Exp2M1,Exp10M1,LogP1,Sinh,Cosh,AsinhandAcoshcan block a thread for seconds to minutes and allocate hundreds of MB on arguments that could come from user input or genericIExponentialFunctions<T>code. That makes it a correctness bug and a denial-of-service risk.PreciseNumber.Exponentials.cs,PreciseNumber.Hyperbolics.cs) and exact alignment inCommonizeSignificands. This belongs to the PreciseNumber math maintainer.Pow(-1, n)overflow) and Sin/Cos/SinCos/Tan at default precision throw ArgumentOutOfRangeException on the library's own constants (e.g. Sin(PreciseNumber.Pi)) #124/Tan refuses precisions that Sin/Cos accept: its 10 internal guard digits are counted against the π reduction ceiling #125 (precision ceilings), which are the same family of failures with large or extreme arguments but separate code paths.Generated by Claude Code