Meta: float accuracy — the standing index
- Type: meta (standing index / charter) — Track U. Never "done"; new float-accuracy work links here.
- Opened: 2026-08-16, at the owner's request: "maybe make a new category (float / exact float), move them tickets over there, let's not waste time on it, and maybe once we collected them all we could decide how to solve this for all cases."
The shape of the category
Deliberately not a new track letter — devdocs/dev/parallel-tracks.md says
resist that, and float accuracy is a kind of work over existing files, not a
new place code lives. It follows the compat precedent: a tag with no
letter, surfaced as a standing index. Each ticket keeps its own track and gate
(lib/rtl/math.pas is B, compiler/builtin/pylib.pas and the backends are A,
NilPy semantics are N) and its own state folder. This page is the grouping.
If ranker support is ever wanted, the cheap form is a float- slug prefix
auto-tagged in tools/progress.py, exactly as feature-opt- gets O. Not done
yet — renaming existing slugs would break [[wiki-links]] across the tree for a
grouping this page already provides.
The central tension — this is what has to be decided ONCE
Two shipped positions, both defensible, currently incompatible:
decide-rtl-math-correctly-rounded-vs-fast-tier(decided) — the RTL is FAST by default, exact only under-dPXX_FLOAT_EXACT. Per the owner, 2026-08-15: 1-2 ulp is a recorded issue, never a bug; error that GROWS with the argument still is.- The NilPy test suite —
test/test_nilpy_math_log.npy,test_nilpy_math_domain_errors.npyand friends assert bit-exact CPython output, because their.expectedfiles are generated by running CPython. And NilPy's contract is upward compatibility with CPython.
Position 1 says a 1-ulp move is not a bug. Position 2 turns it red in CI. Both are currently true of the same binary.
It got sharper on 2026-08-16: bug-a-nilpy-star-star-has-its-own-low-precision-pow
was fixed by routing NilPy's ** to lib/rtl/math.pas's Power (6a4fa40ae) —
correctly, since pylib's own series pow was out by up to 1282 ulp. But that
makes one function serve a fast-by-default Pascal RTL and a CPython-exact
NilPy at the same time, which cannot both be satisfied by one implementation.
The options, sketched (not decided here)
- Two entry points: Pascal's
Powerstays fast, NilPy's**binds to a correctly-rounded one. Costs a second symbol and the risk they drift. - One correctly-rounded implementation, fast tier deleted. Simple, honest, and gives back the speed wins.
- Regenerate the NilPy
.expectedfiles from pxx rather than CPython for transcendentals, with a stated tolerance. Cheapest; weakens the CPython oracle exactly where it is most valuable. - Per-function policy: correctly rounded where CPython parity is observable
(
**,math.*), fast where it is not. Most work, most defensible.
The tickets — the collection
Living list; add rows rather than filing float tickets loose.
Accuracy (ulp) — the core of the category
| ticket | track | prio | note |
|---|---|---|---|
regression-b-power-lost-a-ulp-when-it-got-26x-faster |
B | — | CLOSED 2026-08-16 not-a-bug per the ruling above; tests relaxed to %.14g, both green. Speed win kept. |
bug-nilpy-float-pow-loses-a-ulp-vs-libm |
N | 20 | pylib's own pow; may be closed by 6a4fa40ae — re-measure before working it |
bug-b-rtl-fast-power-needs-a-hi-lo-log (done) |
B | — | the change that caused the regression above; context |
bug-a-riscv32-softfloat-has-no-subnormals |
A | 40 | a target-specific accuracy hole |
Semantics / IEEE behaviour
| ticket | track | prio | note |
|---|---|---|---|
compat-pascal-strict-fpc-unmask-fp-exceptions-two-flags |
A | 30 | already decided in shape (masked by default, FPC behind flags) |
feature-a-expose-rounding-mode-intrinsic-to-pascal |
A | 30 | |
feature-a-extended-is-an-alias-for-double |
A | 25 | umbrella for the Extended cluster — see below |
Formatting — related but a DIFFERENT question, do not conflate
Printing is not accuracy; a correctly-rounded value can still print wrong.
| ticket | track | prio |
|---|---|---|
compat-pascal-write-fixed-huge-magnitude-differs-from-fpc |
A | 40 |
bug-b-write-of-a-real-ignores-the-field-width-without-decimals |
B | 20 |
compat-pascal-writeln-of-a-single-uses-double-width |
A | 30 |
decide-default-float-output-format-and-constant-precision |
U | — |
docs-publish-the-three-language-rounding-table |
D | 30 |
The Extended cluster — consolidated 2026-08-30, work it as ONE session
Owner ruling, 2026-08-30: "eventually we will implement 80-bit extended type properly. for now, we move all related tickets to the float subfolder. so we can work on those tickets in a consolidated session." Three overlapping umbrellas existed in three folders and none referenced the others; they are now one.
Standing policy, owner 2026-08-30: Extended = Double on all targets
for the foreseeable future, and the rest of this cluster waits until it becomes
relevant "or until some mathematician studies the topic and comes up with a
solid plan." If it is ever revived, the recorded direction is software
emulation of IEEE binary128, not x87's 80-bit — see the umbrella.
| ticket | track | prio | role |
|---|---|---|---|
feature-a-extended-is-an-alias-for-double |
A+F | 25 | the umbrella — scope, the four workstreams, the ruling |
feature-extended-type-support |
A+F | 25 | superseded gravestone (kept for three inbound citations) |
decide-is-real-a-double-or-fpcs-80-bit-extended |
U | 30 | residual open question: does bare Real follow Extended on x86-64? |
bug-p-sizeof-extended-disagrees-with-the-storage-extended-gets |
P | 65 | un-parked to backlog/ 2026-08-30 — correctness under the permanent alias, not float work |
This cluster is the clearest instance of what this index exists for. The four
aspects — the 10-byte type with its padding, x87 codegen (the SSE2 path cannot
express 80-bit), Str/Val/FloatToStr/WriteLn, and lib/rtl/math.pas's
deliberately Single+Double-only overload set — are individually tractable and
collectively the point: a 10-byte type nothing can print, or an x87 path no math
routine reaches, is worse than the honest alias shipped today. Hence
collect-do-not-fix-piecemeal, in its strongest form.
Performance (accuracy-adjacent: the reason a fast tier exists at all)
bug-b-strtofloat-is-3600x-slower-than-cpython-for-small-exponents (B, 30),
feature-opt-float-format-fast-path (O, 30),
feature-b-hardware-sqrt-on-aarch64-and-arm32 (B, 20).
RULING 2026-08-16 (owner) — the central tension is resolved: policy wins, tests bend
"i simply don't care about minor float discrepancies and we should stop flagging them." — user, 2026-08-16
That settles "The central tension" above. Of the four sketched options, the one taken is regenerate/relax the assertion where a ulp is all that differs — but in its least destructive form, and the form matters:
- the
.expectedfiles are still generated from CPython, never from pxx; - only the specific values that differ by a ulp are widened, via
"%.14g"; - every other row keeps asserting CPython's exact output.
So the oracle is kept and simply not read past 14 significant digits. Worked
example: [[regression-b-power-lost-a-ulp-when-it-got-26x-faster]], closed as
not-a-bug with test_nilpy_math_log and test_nilpy_math_domain_errors green.
The working rule for everyone, replacing the "collect, do not fix piecemeal" caution below for the accuracy rows: a 1-2 ulp finding is not a bug, is not a ticket, and does not get flagged. If a test goes red on one, the TEST is asserting more than the RTL promises and the test is what changes. Two things are unchanged: error that grows with the argument is still a bug, and NilPy's upward-compatibility contract still binds everywhere the difference is larger than a ulp or two.
This also retires the "one function serving two masters" worry about **
routing to Power — it does not need to serve a bit-exact NilPy, because the
NilPy tests no longer demand bit-exactness at that precision.
Correcting the triage assumption that opened this
The owner's first read was "they only apply to exact float at first glance".
Measured, that does not hold for the top item.
regression-b-power-lost-a-ulp-when-it-got-26x-faster is on the default
path — -dPXX_FLOAT_EXACT is exactly the mode where it is still correct — and
it is what turned test_nilpy_math_log and test_nilpy_math_domain_errors red
at HEAD.
So the category splits three ways, and only the middle one is safe to park:
- default-path accuracy — live, can be red in CI, fix on merit;
- exact-mode-only — genuinely parkable, nothing user-visible by default;
- formatting — its own question, unaffected by either.
Park the middle group. Do not park the first.
Working rule while this is open
Collect, do not fix piecemeal. A one-ulp finding gets a row here and a ticket in its owning lane; it does not get a hand-tuned kernel, because the answer to "how accurate should this be" is the thing being decided and every piecemeal fix pre-empts it. The exception is a landed regression — that is a change of behaviour with a named cause, not a standing approximation, and it is fixed or reverted on its own terms.
2026-08-19 — the owner restated the priority rule for the THIRD time
Verbatim: "the endless flow of floating point issues... which, imho, are low prio. compiler syntax, segfaults, etc, all prio. floating point, especially when 'mostly ok' (apart performance or insignificant digits), very low prio. by definition. today we wasted cycles on that again."
Same position as 2026-08-15 and 2026-08-16. Three restatements of one rule is evidence the rule is not being carried by anything structural — it lived in a coordinator's memory and in this page's prose, and neither is read by the thing that schedules work. Prio raised 40 -> 60 for that reason: this page is not a fix, it is the item whose resolution stops the flow.
Why de-ranking alone will NOT stop it, and this is the actual finding
The float tickets already rank correctly. Measured today: the accuracy tickets sit at p15-p30
(bug-nilpy-complex-pow, bug-nilpy-float-power, feature-b-hardware-sqrt,
feature-opt-float-format-fast-path) and none of them was worked. The cycles did not go
through the ranker. They went through two other doors:
- A red job demands attention regardless of its prio. Position 2 above — the NilPy
.expectedfiles are generated by running CPython, so they assert bit-exact output — means a 1-ulp move is a CI RED, and a red is worked at the priority of being red, not at the priority of its subject. That is the whole mechanism, and it is immune to the prio field. - Cluster grouping bypassed prio. The backlog-shrink push preferred cheap-and-related
work;
TRIAGE-backlog-shrink.mdcluster 2 grouped three float writer tickets as low-hanging fruit, and cheap beat low-prio. The coordinator built that cluster — this is a coordination error, not a worker one.
So the lever is not the prio: field. It is deciding the fast/exact split above, because that
is what decides whether an ulp can turn a job red.
The distinction that must survive the de-ranking
A control-flow or codegen bug that merely carries a float is not a float-accuracy ticket. Today's writer cluster contained both kinds and they should not have shared a prio: "exponent form not correctly rounded" is accuracy (low, by this rule), while "writeln fixed saturates at Int64" is a wrong value at scale and "the writer was called with the wrong number of arguments" is a signature bug that happened to live in float code. Rank the mechanism, never the datatype.
2026-09-02 — known-incompat/ exists now, and it is this category's destination
A new terminal folder landed today: devdocs/progress/known-incompat/. Entry
test — the measurement is TRUE and reproducible, no program observes a wrong
value, and neither implementation is wrong; ours is CHOSEN, never
tolerated. Full contract in that folder's README.
That is the same principle this category already runs on. The owner's 2026-08-15 position — 1-2 ulp is a recorded issue, never a bug; error that GROWS with the argument still is — is a known-incompat statement in all but name. So is the general rule restated 2026-09-02: "compatible with FPC" means the VALUE, not the intermediate's type.
Nothing has been moved, deliberately. The 2026-08-16 directive is "do NOT fix them piecemeal ... once we collected them all we could decide how to solve this for all cases", and sweeping tickets out of the index one at a time dissolves the collection this page exists to be. Classifying is not fixing, but it has the same effect on the index.
What this unblocks
The central tension above is unchanged and is still the thing to decide once:
position 1 says a 1-ulp move is not a bug, position 2's .expected files are
generated from CPython and turn it red in CI. known-incompat/ gives position
1 somewhere to live — a place that records "true, reproducible, and not a
defect" without either pretending the measurement is wrong or leaving it in a
ranked queue forever. It does not resolve the contradiction; it removes the
excuse that there was nowhere to put the answer.
The candidates, for whoever decides — NOT a work list
Two here are not ulp questions at all, so they would survive whatever is decided about accuracy tiers:
bug-b-f-fixed-point-rounding-of-a-tie-goes-down-where-fpc-goes-up—Format('%.2f',[1.005])is 1.00 here, 1.01 in FPC. Ours is arithmetically right: 1.005 as a double is 1.00499999999999989…, so 1.00 is the correct rounding of the value that actually exists. FPC rounds the decimal literal it was written from. Rendering the true value is a defensible choice and it is ours.compat-pascal-a-whole-valued-double-variant-writes-a-trailing-point-zero—15.0vs FPC15, the ticket's own words: rendering only, the value is right.
Neither is proposed for a move today. They are listed so the decision has concrete cases in front of it rather than a category.