Repository navigation
Answer the limit of a signum instead of overflowing the stack (#704) - #705
Merged
Rafael-SOWNet merged 1 commit intoAug 4, 2026
Merged
Conversation
lim x->2 signum(x) killed the process. Signumf was the one node whose limit override handed back an unevaluated limit of the very expression it was asked about, and that is not merely a failure to answer: the two-sided path compares its two one-sided results by evaluating them, evaluating a limit computes it, and computing it arrives back at the same override. Some four thousand frames later the stack runs out, which kills the process rather than raising anything a caller could catch. The sign is constant on either side of zero, so wherever the argument tends to anything but zero the limit is the sign of that -- including at the infinities, where it is 1 and -1. At zero there is nothing to say, since the sign is 1 on one side and -1 on the other and which one a one-sided limit takes depends on the direction the argument approaches from rather than only on what it tends to. Null is returned there rather than a limit of this expression. It means the same thing to the caller, which hands back an unevaluated limit of its own, and it takes the branch that falls through to l'Hopital's rule and returns instead of the branch that evaluates and re-enters. 13 new tests, three of which time out rather than fail without the fix, since what they are really pinning is termination. Suite 4474 passed, 0 failed.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #705 +/- ##
==========================================
+ Coverage 80.99% 81.59% +0.59%
==========================================
Files 155 159 +4
Lines 13687 13793 +106
Branches 1957 2331 +374
==========================================
+ Hits 11086 11254 +168
+ Misses 1990 1885 -105
- Partials 611 654 +43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
lim x->2 signum(x)kills the process. Not an exception — a stack overflow, so a caller cannot guard against it.Signumfwas the one node whose limit override handed back an unevaluatedLimitfof the very expression it was asked about:That is not merely a failure to answer. The two-sided finite path compares its two one-sided results with
ExpressionNumerical.AreEqual, which evaluates them; evaluating aLimitfcomputes the limit; and computing it arrives back at the same override. Some four thousand frames later the stack runs out.abs(x)is fine, becauseAbsfmaps the limit through its argument instead.What it answers now
The sign is constant on either side of zero, so wherever the argument tends to anything but zero the limit is the sign of that — including at the infinities, where
signumis 1 and -1.lim x->2 signum(x)lim x->-2 signum(x)lim x->2 signum(x - 5)lim x->+oo signum(x)lim x->-oo signum(x)lim x->2 signum(x) * xlim x->0 signum(x)At zero there is nothing to say — the sign is 1 on one side and -1 on the other, and which one a one-sided limit takes depends on the direction the argument approaches from rather than only on what it tends to. So that one stays unevaluated, which is the honest answer and, more to the point, terminates.
The one-line reason it stops recursing
nullinstead of aLimitfof itself. It means the same thing to the caller — no reading — and the public API still hands back an unevaluated limit, so nothing about the answer changes for the unsettleable case. Butnulltakes the branch that falls through to l'Hopital's rule and returns, rather than the branch that evaluates the two results and re-enters. Worth stating because it is the whole fix.Tests
13 new. Three of them assert termination rather than a value — they would time out rather than fail without the change, which is the right shape for a former crash: a regression fails the run instead of taking the runner down with it.
Suite:
Failed: 0, Passed: 4474, Skipped: 14, Total: 4488.Independent of #703;
git merge-treereports no conflict between them, though both touchLimit.Classes.csin different places. Found while writing #703, where a new node had the same shape and the same crash — I gave that one a real limit case rather than copying this pattern.