Skip to content

HurwitzZeta and Zeta(s, a) at the engine's precision for real arguments; Part of #340 - #360

Merged
arnog merged 3 commits into
cortex-js:mainfrom
enumeratio:zeta-precision
Sep 28, 2026
Merged

arnog merged 3 commits into
cortex-js:mainfrom
enumeratio:zeta-precision

Conversation

@enumeratio

Copy link
Copy Markdown
Contributor

Part of #340

HurwitzZeta(s, a) and Zeta(s, a) answered at machine precision whatever ce.precision was. For real s and a they now follow it, through a BigDecimal Euler–Maclaurin kernel in the same idiom as bigZeta. Left of Re(s) = 0 the kernel raises its working precision by the cancellation it expects. Past a 1,200-digit budget it falls back to the double kernel. The exact forms and Zeta(s, 1) → bigZeta are unchanged. Complex operands stay at machine precision.

Checked against mpmath (dps 90) over s ∈ [−10, 20] and a ∈ (−10, 10], at precision 30 and 50: worst relative error 5e-29 and 9e-49. About 0.6 ms a call at precision 50.

enumeratio and others added 3 commits September 28, 2026 12:27
…for real s and a, via a bignum Euler-Maclaurin kernel ported from enumeratio's hurwitz-zeta-big.ts

- bigHurwitzZeta/bigZetaGeneralized (numerics/special-functions.ts): real-s,
  real-a Hurwitz zeta via Euler-Maclaurin in BigDecimal, sized by a
  doubles-only cancellation plan (hurwitzZetaBigPlan) so Re(s) < 0 raises
  the working precision instead of cancelling; zetaGeneralized peels
  |k+a|^-s terms for a <= 0 the same way the double kernel does.
- library/arithmetic.ts: evaluateHurwitzZeta/evaluateGeneralizedZeta route
  through the bignum kernel when bignumPreferred and both operands are
  real, falling back to the existing double hurwitzZetaComplex kernel
  otherwise (a complex operand, or precision too extreme for the bignum
  kernel's working-digit budget).
- zeta-values.test.ts: table-driven cases across both heads, positive and
  non-positive a, and Re(s) << 0, at precision 30 and 50, checked against
  mpmath.

Part of cortex-js#340
…he engine precision)

Dual review (Codex + Claude), every value checked against mpmath at the
matching precision.

Correctness:
- The Euler-Maclaurin plan sized an ABSOLUTE error and capped the retry at
  twice the requested digits, so a tiny result was wrong with every digit
  shown: HurwitzZeta(200, 10) at precision 50 was 5.5e-201 (true 1.0e-200)
  and HurwitzZeta(300, 10) was 47% off. The target is now relative to an
  estimate of the result's magnitude, with up to four passes, and the kernel
  returns only a result carrying the requested digits.
- The cancellation allowance did not subtract the result's own magnitude,
  so a very negative s exceeded the 1,200-digit budget and fell back to the
  double kernel, which overflowed: HurwitzZeta(-400.5, 0.3) was +oo and
  (-1000.5, 0.3) NaN. Both now give the correct 50 digits, and past the
  kernel's limit the application stays symbolic instead of +oo/NaN.
- The planner treated a Pochhammer factor that rounds to zero as a double
  as exact termination (s = -2 + 1e-30 lost digits from the 30th place);
  it now reads the exact operand.
- An exact rational operand (1/3) was rounded to the engine precision before
  the guard digits, so the last digit was wrong on the evaluate route; it is
  passed as a pair and converted inside the guarded precision. The .N()
  route still pre-rounds operands engine-wide (ROADMAP).
- A large negative base point allocated one BigDecimal per unit of |a|
  with no deadline: Zeta(2, -1e8) tried 1e8 objects. The prefix is streamed
  with deadline checks and, past 10 000 terms, computed as a Hurwitz
  difference; Zeta(2, -1000000.5) went from 1.9 s to 3 ms.
- An integer s between -100 and 0 answers the exact Bernoulli form, so
  HurwitzZeta(-2.0, 0.5) is 0 (it was -6.9e-18).

Performance: the coefficients reuse the engine's cached Bernoulli table and
a running factorial; the first HurwitzZeta(3, 1/3) at precision 1000 went
from about 11 s to 0.55 s.

Tests and documentation: tests for the tiny, huge, near-integer and 1/3
cases, the fallback, and a machine-precision engine; CHANGELOG under
[Unreleased]; the double kernel's small inaccuracies (recorded in ROADMAP)
and the external-file reference in a comment replaced by the PR number; the
unrelated floorModFloat reformat dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@arnog
arnog merged commit d20fe5d into cortex-js:main Sep 28, 2026
@arnog

arnog commented Sep 29, 2026

Copy link
Copy Markdown
Member

Thanks, merged, with a review pass on your branch as `f6075589. The main corrections:

  • The Euler–Maclaurin plan sized an absolute error and capped the retry at twice the requested digits, so a tiny result was wrong with every digit shown: HurwitzZeta(200, 10) at precision 50 was $5.5 \times 10^{−201}$ (true $1.0 \times 10^{−200}$). The target is now relative to the result's magnitude, with up to four passes, and only a result carrying the requested digits is returned.
  • The cancellation allowance did not subtract the result's own magnitude, so HurwitzZeta(-400.5, 0.3) fell back to the double kernel and gave $+\infty$; it now gives the correct 50 digits, and past the kernel's limit the application stays symbolic.
  • A Pochhammer factor that rounds to zero as a double was treated as exact termination ($s = -2 + 1e-30$ lost digits from the 30th place); an exact rational $a$ was rounded before the guard digits (HurwitzZeta(3, 1/3) had a wrong last digit on the evaluate route); a large negative base point allocated one BigDecimal per unit of $|a|$ (Zeta(2, -1e8)); all fixed.
  • The coefficients reuse the engine's cached Bernoulli table with a running factorial: the first HurwitzZeta(3, 1/3) at precision 1000 went from about 11 s to 0.55 s.

Two open notes in ROADMAP: .N() rounds an exact rational operand to the engine precision before a kernel adds guard digits (engine-wide, not this PR), and the double kernel's small inaccuracies at machine precision.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants