Skip to content

fix: avoid premature overflow in math/base/special/expm1 - #16239

Open
Thanatos9404 wants to merge 1 commit into
stdlib-js:developfrom
Thanatos9404:fix/expm1-overflow-threshold
Open

Thanatos9404 wants to merge 1 commit into
stdlib-js:developfrom
Thanatos9404:fix/expm1-overflow-threshold

Conversation

@Thanatos9404

Copy link
Copy Markdown
Contributor

Resolves #12468.

Description

What is the purpose of this pull request?

This pull request:

  • Changes the overflow check in @stdlib/math/base/special/expm1 from y >= OVERFLOW_THRESHOLD to y > OVERFLOW_THRESHOLD in both the JavaScript and C implementations, matching FDLIBM and FreeBSD (if(x > o_threshold) return huge*huge;) and the existing implementation notes ("if x > 7.09782712893383973096e+02, then expm1(x) will overflow").
  • As a result, expm1( 709.782712893384 ) now returns 1.7976931348622732e+308 (the same as exp( 709.782712893384 ) and Math.expm1( 709.782712893384 )) instead of Infinity.
  • Adds tests (JavaScript and native) for the threshold, the previous double, and the next double.

Related Issues

Does this pull request have any related issues?

This pull request has the following related issues:

Questions

Any questions for reviewers of this pull request?

No.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

@kgryte I noticed this is assigned to you, so feel free to close this if you already have a fix in progress.

For x equal to the threshold, argument reduction yields k = 1024, which goes through the existing branch that adds k to the exponent of y directly (rather than multiplying by 2^k), so no intermediate value overflows.

Expected values in the new tests come from mpmath at 400 bits of precision, rounded to nearest:

x exact expm1(x) expected
709.7827128933839 1.79769313486206884...e+308 1.7976931348620688e+308
709.782712893384 1.79769313486227322...e+308 1.7976931348622732e+308
709.7827128933841 1.79769313486247759...e+308 Infinity (exceeds 1.7976931348623157e+308)

Both implementations were also compared against mpmath on 7,004 inputs (the threshold, the 2,000 doubles below it, the three above it, and random inputs in [600, 710] and in the k = 1024 band): the maximum error is 1 ULP and every overflow matches the reference. The C implementation (built with gcc -std=c99 -Wall -Wextra -pedantic at -O0 and -O3) returns the same bits as the JavaScript implementation on all of them.

expm1rel (#12436) is unaffected: for x equal to HUGE_VALUE it already takes the expm1( x/2 ) branch, which returns the same value.

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

If you answered "yes" to using AI assistance, please provide a short disclosure indicating how you used AI assistance. This helps reviewers determine how much scrutiny to apply when reviewing your contribution. Example disclosures: "This PR was written primarily by Claude Code." or "I consulted ChatGPT to understand the codebase, but the proposed changes were fully authored manually by myself.".

Written primarily with Claude Code; the new values were checked against a high-precision reference.


@stdlib-js/reviewers

Previously, `expm1` returned `+infinity` when `x` was exactly equal to
the overflow threshold `709.782712893384`, which is the double-precision
floating-point number nearest to, and just below, `ln(MAX_VALUE)`. The
exact result there is about `1.79769313486227322e+308`, which is finite
and about 213 ULPs below `1.7976931348623157e+308`, and `exp` already
returns `1.7976931348622732e+308` for the same input. As a result,
`expm1` overflowed for an input at which the smaller function `exp`
did not.

The FDLIBM and FreeBSD implementations only return `+infinity` when
`x` is strictly greater than the threshold, and the implementation
notes in this package already state that `expm1(x)` overflows for
`x > 7.09782712893383973096e+02`. This commit updates both the
JavaScript and C implementations to use a strict comparison. For `x`
equal to the threshold, argument reduction yields `k = 1024`, which is
handled by the existing branch that adds `k` to the exponent of `y`
directly, so no intermediate value overflows, and the function now
returns the correctly rounded value `1.7976931348622732e+308`.

Closes: stdlib-js#12468
Ref: http://www.netlib.org/fdlibm/s_expm1.c
@Thanatos9404
Thanatos9404 requested a review from a team October 18, 2026 17:41
@stdlib-bot stdlib-bot added Math Issue or pull request specific to math functionality. Needs Review A pull request which needs code review. First-time Contributor A pull request from a contributor who has never previously committed to the project repository. labels Oct 18, 2026
@stdlib-bot stdlib-bot removed the First-time Contributor A pull request from a contributor who has never previously committed to the project repository. label Oct 29, 2026

This branch has not been deployed

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

Labels

Math Issue or pull request specific to math functionality. Needs Review A pull request which needs code review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: math/base/special/expm1 overflows prematurely

2 participants