Skip to content

1^oo evaluates to 1, so lim x->+oo (x-5)^x / x^x gives 1 where it is e^(-5) #738

Closed

Description

@Rafael-SOWNet

1 ^ oo is an indeterminate form, and AngouriMath evaluates it as 1:

"1 ^ (+oo)".ToEntity().EvalNumerical()   // 1

That reaches the limits machinery as a wrong answer:

is gives
lim x->+oo (x - 5) ^ x / x ^ x e ^ (-5) 1
lim x->+oo (x + 1) ^ x / x ^ x e 1

Written as a single power the same limits are correct, which is what locates the cause:

gives
lim x->+oo ((x - 5) / x) ^ x 1 / e ^ 5 ✅
lim x->+oo (1 - 5/x) ^ x 1 / e ^ 5 ✅

And the neighbouring quotients, where the base ratio does not tend to 1, are right: (2 * x) ^ x / x ^ x gives +oo, and (x ^ 2) ^ x / x ^ x gives +oo.

Cause

Two things, and they are separable.

The ordering. ApplySecondRemarkable -- the rule that reads a 1 ^ oo -- runs at the top of LimitFunctional.ComputeLimit, before anything is simplified. At that point (x - 5) ^ x / x ^ x is a Divf, not a 1 ^ oo, so the rule has nothing to match on. The descent's Divf case then calls ComputeLimitImpl, which begins with expr.Simplify(), and that turns the quotient into ((x - 5) / x) ^ x -- a 1 ^ oo which nothing will now re-read, because the rule that reads one has already run.

The evaluation. Substitution then puts x -> +oo into it, and the evaluator answers 1 ^ (+oo) = 1 rather than declining.

Simplify() is right here and the second remarkable limit is right here; what is wrong is that a form created by simplification is judged by rules that ran before it existed.

Note on the fix

The second half reaches everything that evaluates a power, not just limits, so it wants its own measurement rather than being bundled into a limits change -- 1 ^ oo giving NaN is correct for a limit but a much wider change than this issue. The first half is narrower: re-applying the remarkable limits after the simplification inside ComputeLimitImpl, or matching the quotient shape before it is simplified.

Found while checking that #736 had not traded one wrong answer for another. It had not -- this reproduces on stock master (318ac9f3) independently of that change.

Activity

  1. Rafael-SOWNet commented on Aug 5, 2026

    @Rafael-SOWNet
    MemberAuthor

    Traced both halves. There are two independent routes to the wrong 1, and which one an expression takes depends on whether substituting the destination yields NaN or an exact 1. That is why the two examples in the report behave differently under a partial fix.

    Route 1 -- SolveBySubstitution

    (x + 1) ^ x / x ^ x simplifies to (1 + 1/x) ^ x. Substituting x -> +oo:

    1 + 1/(+oo)               =>  1        (exactly, since 1/(+oo) is 0)
    (1 + 1/(+oo)) ^ (+oo)     =>  1
    

    So LimitSolvers.SolveBySubstitution gets a clean finite value and returns it. Note that lim x->+oo (1 + 1/x) ^ x on its own is correct (e) -- it is caught by ApplySecondRemarkable at the top of ComputeLimit. The quotient form only becomes a 1 ^ oo after the Simplify() inside ComputeLimitImpl, by which time that rule has already run.

    Route 2 -- the Powf descent

    (x - 5) ^ x / x ^ x simplifies to ((x - 5) / x) ^ x, and substituting there gives (+oo - 5) / (+oo) = NaN, so SolveBySubstitution correctly declines. The 1 comes from Powf.ComputeLimitDivideEtImpera instead (Limit.Classes.cs), whose switch has

    ({ IsFinite: true } lim1, { } exp) => New(lim1, exp),

    Reaching that case means the exponent's limit is not finite, so with a base tending to 1 it builds 1 ^ x -- and 1 ^ x simplifies to 1. The neighbouring case ({ } bas, { IsFinite: true } lim2) has the same hazard for oo ^ 0.

    This is exactly what the IsDeterminate remark in Transformations.cs already warns about:

    Powers are not asked this question. The arithmetic answers 1 ^ (+oo) with 1 and (+oo) ^ 0 with 1, and as limits both are indeterminate [...] so a power settled this way would be settled wrongly.

    IsDeterminate is kept away from powers for that reason, but the Powf descent substitutes a part's limit without consulting it.

    What a fix needs

    Both routes, and they are the same principle -- do not let the arithmetic's reading of an indeterminate power stand as a limit:

    1. SolveBySubstitution must decline when the substituted expression holds a 1 ^ oo or oo ^ 0.
    2. The Powf descent must not replace a base whose limit is 1 while the exponent still diverges, nor an exponent whose limit is 0 while the base diverges.

    I implemented (2) alone and measured it, which is what separated the routes:

    master with (2) only
    (x - 5) ^ x / x ^ x (is e ^ (-5)) 1 unevaluated
    (x + 1) ^ x / x ^ x (is e) 1 1 -- unchanged
    ((x - 5) / x) ^ x 1 / e ^ 5 ✅ 1 / e ^ 5 ✅
    (1 + 1/x) ^ x e ✅ e ✅

    So (2) turns one wrong answer into no answer and leaves the other untouched. I have not raised a PR for it: half of this class would look fixed while the other half was still wrong, which is worse than the current state being plainly wrong.

    Sequencing note

    With both guards in, these expressions fall through to SolveAsIndeterminatePower (which wants an exponent tending to 0, so it declines here), l'Hopital, SolveByRewriting and finally Gruntz. Gruntz is the one that should answer them -- but only with #736 in, since (1 + 1/x) ^ x is a moving-exponent power and that is precisely what #736 fixes the reading of. So this is best done on top of #736 rather than in parallel with it, or the guards will convert wrong answers into missing ones rather than into right ones.

    Also worth recording: 1 ^ oo = 1 is the IEEE 754 convention for pow, so the evaluator is not simply wrong to do it -- which is why the fix belongs in the limits path rather than in the arithmetic.

  2. Rafael-SOWNet commented on Aug 5, 2026

    @Rafael-SOWNet
    MemberAuthor

    The ordering half of this is fixed in #739. The evaluation half — 1 ^ (+oo) giving 1 — is untouched, so this issue stays open for it.

    The fix is the narrower of the two options the report suggested: re-applying the remarkable limits after the simplification, rather than matching the quotient shape before it. The rule is asked a second time inside SimplifyAndComputeLimitToInfinity, which is where the expression is in the form the solvers read it in and where every destination has already been normalised to +oo.

    Worth recording, because the earlier tracing predicted otherwise on both counts:

    • It closes both routes at once. Both SolveBySubstitution (where (1 + 1/x)^x substitutes to an exact 1^(+oo)) and the Powf descent (where ((x - 5)/x)^x builds 1^x, which simplifies to 1) run through that one point after the simplification has created the form, so a single re-read catches them both. The half-fixed state that made the earlier attempt not worth raising does not arise.
    • It does not need Compare a moving-exponent power against the right thing (#735) #736 underneath it. The expectation was that guarding these would convert wrong answers into missing ones unless Gruntz could pick them up. It does not come to that — the rewrite answers them outright rather than dropping them through.

    Measured at x -> +oo, against stock master (318ac9f3):

    is was now
    (x - 5)^x / x^x e^(-5) 1 1/e^5 (309 ms)
    (x + 1)^x / x^x e 1 e (16 ms)
    (x + 3)^x / x^x e^3 1 e^3 (21 ms)
    x^x / (x + 1)^x e^(-1) 1 1/e (57 ms)
    x^x / (x - 5)^x e^5 1 e^5 (50 ms)
    (2x + 1)^x / (2x)^x e^(1/2) 1 sqrt(e) (59 ms)
    (x - 1)^x / x^x e^(-1) unevaluated, 5.2 s 1/e, 163 ms

    The neighbouring quotients are untouched: (2x)^x / x^x and (x^2)^x / x^x still +oo, x^x / (2x)^x still 0, and the single-power forms that were always right still are.

    One case in the same family it does not reach: (x^2 + 1)^x / (x^2)^x, where Simplify never gathers the quotient into a single power, so no 1^oo is created for anything to re-read. That one declines rather than answering wrongly, and its cause is in the simplification — filed as #740.

    Full suite 4909 passed / 0 failed, F# 130/130, corpus 112/117 with 0 wrong, 0 error, 0 timeout.

  3. Rafael-SOWNet commented on Aug 6, 2026

    @Rafael-SOWNet
    MemberAuthor

    Status update: both limits this issue reported now answer correctly, and what is left is not a defect.

    Measured on stock master (184dd5a):

    is gives
    lim x->+oo (x - 5) ^ x / x ^ x e ^ (-5) 1 / e ^ 5 ✅
    lim x->+oo (x + 1) ^ x / x ^ x e e ✅

    The neighbouring cases from the report are still right too — (2 * x) ^ x / x ^ x and (x ^ 2) ^ x / x ^ x both give +oo.

    The ordering half landed in #739, and the remaining wrong answers in this family were closed by the indeterminate-power work in #760 and the Stirling series in #764/#766.

    The evaluation half is unchanged and I do not think it should be treated as a bug:

    "1 ^ (+oo)".ToEntity().EvalNumerical()   // still 1

    That is a convention rather than an error. SymPy answers 1; Mathematica answers Indeterminate. Both are defensible, and the choice has consequences beyond this issue, so it is a maintainer's call rather than mine.

    What has changed is that nothing depends on it any more: the two wrong answers this issue was filed for are gone, and I know of no remaining wrong answer that comes from 1 ^ oo evaluating to 1.

    So this can be closed as reported-and-fixed, or kept open narrowed to "should 1 ^ oo be 1 or indeterminate" — whichever you prefer. I have not changed the evaluation and will not without a decision here.

  4. Rafael-SOWNet commented on Aug 8, 2026

    @Rafael-SOWNet
    MemberAuthor

    Closing: the behaviour this issue reports is fixed. Re-measured on master (21f0d16) rather than relying on the earlier comment:

    reported now
    lim x->+oo (x - 5) ^ x / x ^ x 1 1 / e ^ 5 ✅
    lim x->+oo (1 + 1/x) ^ x — e ✅

    e^(-5) is what the title asks for.

    On the part that has not changed: 1 ^ (+oo) still evaluates to 1. That is deliberate rather than overlooked — it is the SymPy and IEEE 754 convention (pow(1, ∞) = 1), and the limit machinery no longer routes through it: the indeterminate form is now detected and resolved before evaluation gets a chance to return 1. That is why the limits are right while the evaluation is unchanged.

    If the convention itself should change, that is a separate decision with a much wider blast radius than this issue, and worth its own thread.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions