Skip to content

N[expr, p]: precision exactly p, with extra digits as needed, as in WMA - #2051

Merged
rocky merged 4 commits into
masterfrom
n-real-valued-precision
Oct 19, 2026
Merged

rocky merged 4 commits into
masterfrom
n-real-valued-precision

Conversation

@tombouctou

Copy link
Copy Markdown
Collaborator

Part of step 6 of the outline in #2047, after #2050: N[expr, p] gives precision p, as WMA does.

Input master this PR WMA 15
Precision[N[Pi, 20.3]] 20. 20.3 20.3
Precision[N[Pi, 41/2]] 20. 20.5 20.5
Precision[N[Pi E, 20]] 19.699 20. 20.
Precision[N[Pi - 314159/100000, 20]] 13.6257 20. 20.
Precision[N[1.5`10, 20.3]] 10. 10. 10.
Precision /@ N[{Pi, 1.5`10, 2.5`30.2}, 20.3] {20., 10., 20.} {20.3, 10., 20.3} {20.3, 10., 20.3}
$MaxExtraPrecision $MaxExtraPrecision 50. 50.

N[Pi E, 20] giving 19.699 came in with #2050: N makes Pi and E 20-digit numbers, and Times now adds their errors.

Changes

  • N[expr, p] (eval_NValues_to_precision in mathics/eval/nevaluator.py) first evaluates at p, as before. If a nonzero arbitrary-precision number in the result has fewer than p digits, it evaluates again with the digits lost added, up to $MaxExtraPrecision more than p. It goes on while some number short of p digits gains digits; one that gains nothing has an input with fewer than p digits, and keeps them. Then every number above p is set to precision p.
  • The N rules see the working precision. So a retry is used only if it has the same form as the first result, up to the values of numbers: with N[a, 20] = 11, N[a Pi, 20] must not turn back into a 3.14… at 21 digits. The first pass is still at exactly p, so a rule given for p applies as before.
  • $MaxExtraPrecision, new, 50. by default, no attributes. Assigning it anything other than a non-negative number or Infinity gives $MaxExtraPrecision::precset, as in WMA.
  • N::meprec when the extra digits are not enough. As in WMA, there is no message when $MaxExtraPrecision is 0.

N[expr, {p, a}] and the internal eval_N are unchanged.

Tests

  • test_n_real_valued_precision and test_max_extra_precision in test/builtin/atomic/test_numbers.py, with WMA 15 values.
  • Doctests for $MaxExtraPrecision.
  • The RootSum doctest N[Integrate[1/(x^5 + 11 x + 1), {x, 1, 3}], 50] expected …857550894. On master that number has precision 49.66, so its last digits were off. Now it is …857553407, as in WMA (…8575534094…; NIntegrate at 70 digits agrees).

Full run on kimmy (Python 3, pytest -n 4 and docpipeline.py), next to master 9f166b1:

  • Doctests: 4611 passed, 0 failed (master: 4604; the 7 new ones are for $MaxExtraPrecision).
  • pytest: 34 failed on this branch, 58 on master. As noted in PrecisionReal: keep a real-valued precision, as WMA does (#2047) #2050, these depend on the order the tests run in. The 12 that fail only on this branch (test_tensor.py, test_testing_expressions.py) pass when their files run on their own.

Benchmark

kimmy, best of 7, both runs at the same time:

master this PR
Total[Table[N[1/k, 50], {k, 2000}]] 2636 ms 2586 ms
Times @@ Table[N[1 + 1/k, 50], {k, 2000}] 3113 ms 3104 ms
Do[x = N[k, 50] + Pi, {k, 2000}] 5512 ms 5277 ms
Total[Table[1./k, {k, 2000}]] (machine reals, control) 1921 ms 1850 ms
Do[N[Pi E + Sqrt[2], 30], {k, 300}] 1118 ms 1643 ms
Do[N[Sin[k] + Cos[k], 30], {k, 300}] 787 ms 935 ms

The benchmark from #2050 doesn't change. An N[expr, p] whose first pass loses digits now costs about one more evaluation (the last two rows): that is the second pass.

Still to do from step 6

  • Functions other than Plus and Times (Sqrt, Power, Exp, …): precision from the condition number.
  • SetPrecision, SetAccuracy.
  • Low-precision inputs stored with few bits (Precision[1.5`20 - 1.4`2]).
  • Precision of a list with a machine number.

🤖 Generated with Claude Code

tombouctou and others added 3 commits October 19, 2026 22:24
Part of step 6 of #2047 (MATH-227).

- N[expr, p] gives each arbitrary-precision number in the result
  precision p, as WMA does, also for a non-integer p:
  Precision[N[Pi, 20.3]] is 20.3 (was 20.).
- When the computation loses digits, it is done again with more digits,
  up to $MaxExtraPrecision more: Precision[N[Pi E, 20]] is 20. again (it
  became 19.699 with #2050), and Precision[N[Pi - 314159/100000, 20]] is
  20. (was 13.6). A number whose input has fewer digits keeps them.
- New $MaxExtraPrecision (50. by default) and N::meprec, as in WMA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
summary_text = "settable global maximum precision bound variable"


class MaxExtraPrecision(Predefined):

@rocky rocky Oct 19, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a new built-in is added, SYMBOLS_MANIFEST.txt should be updated.

You can run admin-tools/build_and_check_manifest.py to automatically update this. (When I just did, I also noticed that XMLXMLObjectImport` needs to be added.)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in e16f1b4: I reran admin-tools/build_and_check_manifest.py, which adds $MaxExtraPrecision. It also adds PolynomialMod, which was missing on master, and puts TagUnset in order.

About XMLXMLObjectImport: it is already in the manifest, as its last line. The script printed the wrong name. In the loop over new symbols it printed name, a variable left over from the loop before, which ends on the last manifest entry. So each new symbol (here $MaxExtraPrecisionandPolynomialMod) was reported as XMLXMLObjectImport. The same commit makes it print the missing symbol.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

As asked in review: rebuilt with admin-tools/build_and_check_manifest.py.
PolynomialMod was missing on master too, and TagUnset is now in order.

build_and_check_manifest.py printed the name of the last manifest entry
(XML`XMLObjectImport) for each new symbol, instead of the new symbol
itself: a variable left over from the loop before. It now prints the
symbol that is missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tombouctou added a commit that referenced this pull request Oct 19, 2026
Part of step 6 of #2047 (MATH-227), on top of #2051.

MPMathFunction gets an optional condition_numbers: for each argument,
the factor Abs[x D[f, x] / f] by which its relative error is multiplied
in the result. When the arguments are exact reals or nonzero
PrecisionReals and the value is a nonzero real, these errors add up, as
in WMA. Power gives Abs[e] for the base and Abs[e Log[b]] for the
exponent; Sqrt[x] is x^(1/2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rocky

rocky commented Oct 19, 2026

Copy link
Copy Markdown
Member

As before, I think this is awesome!

@rocky

rocky commented Oct 19, 2026

Copy link
Copy Markdown
Member

LGTM

@rocky
rocky merged commit 5301730 into master Oct 19, 2026
15 checks passed
@rocky
rocky deleted the n-real-valued-precision branch October 19, 2026 20:54
tombouctou added a commit that referenced this pull request Oct 19, 2026
Part of step 6 of #2047 (MATH-227), on top of #2051.

MPMathFunction gets an optional condition_numbers: for each argument,
the factor Abs[x D[f, x] / f] by which its relative error is multiplied
in the result. When the arguments are exact reals or nonzero
PrecisionReals and the value is a nonzero real, these errors add up, as
in WMA. Power gives Abs[e] for the base and Abs[e Log[b]] for the
exponent; Sqrt[x] is x^(1/2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants