← board

Meta: float accuracy — the standing index

The shape of the category

Deliberately not a new track letterdevdocs/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:

  1. 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.
  2. The NilPy test suitetest/test_nilpy_math_log.npy, test_nilpy_math_domain_errors.npy and friends assert bit-exact CPython output, because their .expected files 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)

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

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:

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:

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:

  1. A red job demands attention regardless of its prio. Position 2 above — the NilPy .expected files 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.
  2. Cluster grouping bypassed prio. The backlog-shrink push preferred cheap-and-related work; TRIAGE-backlog-shrink.md cluster 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:

Neither is proposed for a move today. They are listed so the decision has concrete cases in front of it rather than a category.