Floating-point error has a phase transition
Supplied working report; original verdicts not validated.
Review status: Needs review. This is an archival source note, not a passed proof.
README.md
# Floating-point error has a phase transition **Preprint draft — not peer reviewed.** Test 10 of the Incoprea hypothesis-test suite, 30 October 2026. Designed and executed with AI assistance (DeepSeek, via its coding agent). The proposal is *Original Theory Notebook*, Volume II, entry 23; the claim as the brief renders it and the pre-specified criteria are quoted verbatim in `hypothesis.md`. ## The result The two claims part company here. The first survives — more cleanly than the brief expected. For `v_i = 2^(−⌊i/k⌋)`, reordering the sum is bit-identical up to a depth that can be written down in advance, and divergent immediately after. The switch is exactly where the arithmetic puts it: 54, 104, 204, 400 and 784 terms for `k = 1, 2, 4, 8, 16` in binary64, and 25, 46, 88, 168 and 320 in binary32, with an offset of **zero** terms in every case (`threshold.max_abs_offset_binary64` = 0, `threshold.max_abs_offset_binary32` = 0) and a precision shift of `29k` terms in every case (`threshold.shift_matches_29k_all`). What the switch is *not* is catastrophic. At the threshold the 200 orderings produce exactly two distinct results, one unit in the last place apart (`threshold.distinct_at_threshold_range` = [2, 2]; at most 8 at the threshold plus sixteen terms, `threshold.distinct_at_threshold_plus_16_range`). The largest relative error against the exact rational sum there is 7.40e-17 in binary64 and 3.97e-8 in binary32 (`threshold.max_relerr_at_threshold_binary64`, `threshold.max_relerr_at_threshold_binary32`) — two-thirds of one unit roundoff in both precisions (`threshold.max_relerr_at_threshold_over_u64` = 0.667). One term shallower the error is zero, because every partial sum is exactly representable (`threshold.max_relerr_below_threshold_binary64`). So the error's dependence on depth is a step from 0 to one ulp, while its dependence on conditioning is a ramp of almost seven orders of magnitude. Holding `κ = Σ|x|/|Σx|` fixed and raising the depth a hundredfold moves the relative error by a factor of 0.96, 0.93 and 0.94 at `κ = 10³, 10⁶, 10⁹` (`conditioning.depth_ratio`); raising `κ` from 1 to 10⁹ at fixed depth moves it by a factor of 7.6e6 (`conditioning.kappa_growth_depth_10000`). Depth is not the controlling variable. Conditioning is. ## Table 1 — where the orderings stop agreeing | k | predicted b64 | observed b64 | predicted b32 | observed b32 | offset | |---:|---:|---:|---:|---:|---:| | 1 | 54 | 54 | 25 | 25 | 0 | | 2 | 104 | 104 | 46 | 46 | 0 | | 4 | 204 | 204 | 88 | 88 | 0 | | 8 | 400 | 400 | 168 | 168 | 0 | | 16 | 784 | 784 | 320 | 320 | 0 | Observed is the first depth at which 200 random orderings of the prefix are not all bit-identical (`threshold.table`, `threshold.n_orderings`). The binary32 offset is 0 for every `k` as well. Figure: `figures/threshold.png`. ## Where the threshold comes from Every term is a power of two, so nothing in the data rounds. After groups `j = 1..J` the running sum is `S_J = (k−1) + k(1 − 2^−J) = 2k − 1 − k·2^−J`, because the `j = 0` group holds only `k−1` terms; it approaches `2k − 1` and lies in the binade `[2^e, 2^(e+1))` with `e = log2 k` (`k = 1`: no unit terms, the sum approaches 1, `e = −1`). A partial sum whose largest index is `J` is a multiple of `2^−J` and is below `2^(e+1)`, so it needs at most `e + 1 + J` significand bits and is exact iff `J ≤ 52 − e`. While that holds, every addition is exact and **every** ordering returns the exact sum: below the threshold, agreement is not approximate, it is forced. At `J = 53 − e` the top of the binade needs 54 bits, the first rounding appears, and its direction depends on where in the sequence it lands. Group `J` begins at index `kJ`, hence `N* = k(53 − e)` in binary64 and `k(24 − e)` in binary32, with the shift `29k` independent of `e`. The brief's sketch (`2^−j < ε·S`, `S ≈ 2k`) is a factor of four loose, because absorption is tested against half an ulp, `2^(e−53)`. It predicts 51, 100, 196, 384 and 752 terms, short by `2k` — 3 when `k = 1` — and at most 32 (`threshold.max_abs_offset_from_brief_binary64`). Judged against the sketch, criterion 1's five-term tolerance fails for `k ≥ 4` (`criteria.c1_switch_within_5_terms_of_brief_sketch` = false); judged against the derived index it holds. Both are reported. ## The conditioning test Terms are generated so that `κ` takes the prescribed value: `n/2` positives `1 + U(0,1)`, and negatives `−ρ` times each positive, with `ρ = (κ−1)/(κ+1)`. That gives `κ` exactly, independently of `n`; `κ = 1` is the no-cancellation baseline. Errors are measured against the exact rational sum. | κ | n = 100 | n = 10 000 | ratio | error/(u·κ) at 10 000 | |---:|---:|---:|---:|---:| | 1 | 2.32e-16 | 1.90e-15 | 8.31 | 17.1 | | 10 | 2.61e-16 | 2.25e-15 | 8.71 | 2.03 | | 10³ | 1.59e-14 | 1.51e-14 | 0.96 | 0.136 | | 10⁶ | 1.53e-11 | 1.42e-11 | 0.93 | 0.128 | | 10⁹ | 1.56e-8 | 1.45e-8 | 0.94 | 0.131 | (`conditioning.summary`, `conditioning.depth_ratio[].mean_of_ratios`; each cell averages `conditioning.n_replicates` = 10 term draws of `conditioning.n_orderings_per_replicate` = 400 orderings.) Below `κ = 10³` there is nothing to amplify: both depths sit at the rounding floor of order 10⁻¹⁵, and the ratios 8.31 and 8.71 are ordinary `u·√n` accumulation, not a depth effect. From `κ = 10³` upward the error is 0.128–0.136 times `u·κ`, the same at both depths to within 8 per cent. Figure: `figures/conditioning.png`. ## Sensitivity Re-running with the terms in construction order — all positive terms, then all negative ones — instead of shuffled multiplies the relative error by about 8 at `κ = 10⁹` (1.22e-7 at depth 100) and turns the depth ratio into 6.70 (`conditioning.sensitivity_natural_order["1000000000"]`). Flatness in depth is a property of random orderings, not of every ordering: depth matters only in so far as it enlarges intermediate partial sums, and a monotone order lets that enlargement accumulate. The `κ`-scaling survives the change — at fixed depth the error still rises from 2.67e-16 to 1.22e-7 across `κ = 1 → 10⁹` — and there is still no break in the error at `N*`. ## Verdicts **Reproducibility threshold: supported.** Criteria 1, 2 and 3 hold (`criteria.c1_switch_within_5_terms_of_derived_prediction`, `criteria.c2_shift_equals_29k`, `criteria.c3_discrete_not_continuous`), subject to the caveat above. **Catastrophe: refuted.** At fixed `κ` the relative error is flat in depth, and at fixed depth it grows with `κ` by a factor of 7.6e6 (`criteria.refute_flat_in_depth_above_floor`, `criteria.refute_grows_with_kappa`). The transition is real — from exact reproducibility to last-bit irreproducibility — and its amplitude is one unit roundoff. ## What this test cannot see The sequence is dyadic by construction, which is what makes the threshold separable from ordinary rounding noise — and means the numbers say nothing about sums whose data already carry rounding from the first term. Nothing here describes the behaviour of numerical error in real solvers with iterative refinement, where an error feeds back into later steps, or with extended precision, where the same 29-bit-per-octave argument predicts a threshold further out that this test does not try to confirm. The condition number is that of a single sum, not a matrix. The conditioning result averages over random orderings; a monotone order behaves differently. And the whole test is one sequence family at one seed: sharpness is proved, position is measured, and neither is a claim about any physical or algorithmic system. ## Files and reproduction `run.py` regenerates `data.csv` (`threshold.n_scan_rows` = 632 distinct-result counts by depth), `data_conditioning.csv` (`conditioning.n_per_ordering_rows` = 40 000 per-ordering errors), `results.json` and both figures, well under the three-minute budget (`runtime_seconds`), with no network or input files. Precision constants sit under `environment` in `results.json`. Every number above is in `results.json` under the key named beside it.
hypothesis.md
# Hypothesis — Test 10, floating-point threshold
Status: pre-specified design and decision rules. The sequence, the five-term
tolerance, the 29k shift and the 20% flatness band are fixed by the brief
`briefs/10-floating-point-threshold.md`; the decision rules below and the
derivation of `N*` were fixed before the run that produced `results.json`, and
none of them was changed afterwards.
## Source proposal
*Original Theory Notebook*, Volume II, entry 23, "Floating-Point Error Has a Phase
Transition", published at
<https://incoprea.com/research-library/original-theories-vol2-20261030/>.
No copy of the volume was retrieved for this test (see `SOURCE.md`). What is quoted
below is the claim **as the brief renders it**, labelled there as the compressed
form of the entry. It is quoted verbatim from the brief, not from the volume.
## The claim, verbatim
> The volume's claim, in compressed form: floating-point error is not a smooth
> function of how much arithmetic is done — there is a critical depth past which
> results stop being reproducible, and the transition has the character of a phase
> change, with the critical depth set by the decay rate of the terms relative to
> machine precision.
## What the volume claims, and what is being separated from it
The sentence packs three things together that do not have to stand or fall
together:
1. **A reproducibility property.** Past some depth, the *result depends on the
order of the additions*: two orderings that agreed bit-for-bit below the
depth disagree above it. This is a statement about the relation between a
computation and the same computation re-arranged. It is arithmetic, and it is
provable.
2. **A sharpness claim.** The change is a switch rather than a gradient — a phase
change, in the volume's phrasing.
3. **A magnitude claim.** That this switch is *catastrophic loss of accuracy*:
that depth beyond the critical value produces large relative error against the
true sum.
(1) and (2) are one hypothesis. (3) is a different one, and it is the one the
word "catastrophe" carries. This test measures (1)+(2) and (3) on the same runs
and reports them as two verdicts, because a system can perfectly well be
irreproducible in its last bit while remaining accurate to within one unit
roundoff — and that is exactly what the arithmetic below predicts.
Two things are explicitly **not** being tested, and the report does not claim
them: whether the threshold behaves the same way for data that carry their own
rounding from the first term (the sequence here is dyadic, so it does not), and
whether real solvers with iterative refinement, compensated summation or extended
precision inherit the threshold (they have different error models).
## Design, as pre-specified
Sequence `v_i = 2**(−floor(i/k))` for `i = 1..N`, `k ∈ {1, 2, 4, 8, 16}`. The
exact total is `2k − 1` in the limit, so "≈ 2k" as the brief says. Every term is a
power of two and every partial sum is a dyadic rational, so the data contribute
no rounding of their own: any divergence observed is produced by addition. This
choice is deliberate and is the reason the threshold is separable from ordinary
rounding noise at all.
For each `k` and each depth `N`: 200 random orderings of `{v_1..v_N}`, summed left
to right, in binary64 (Python `float`) and in binary32 (`numpy.float32`); count
the distinct results; the first depth at which they are not all equal is the
observed threshold. Exact sums are `fractions.Fraction`. Seed `20261030`.
Conditioning experiment: sums of `n` terms with controlled cancellation,
`κ = Σ|x_i| / |Σx_i| ∈ {1, 10, 10³, 10⁶, 10⁹}`, at `n ∈ {100, 10000}`, with
relative error measured against the exact rational sum. 400 random orderings per
replicate, 10 independent draws of the terms per cell (a single draw gives a
depth ratio that swings by ±20% between realisations; the replicate mean is the
stable quantity, and the replicate range is reported).
## The predicted threshold, derived before measurement
After the groups `j = 1..J`, the running sum is
S_J = (k−1) + k(1 − 2^−J) = 2k − 1 − k·2^−J,
because the `j = 0` group holds only `k−1` terms. So `S_J` approaches `2k − 1`
and, for `k ≥ 2`, lies in the binade `[2^e, 2^(e+1))` with `e = log2 k`
(for `k = 1` there are no unit terms, the sum approaches 1, and `e = −1`).
A partial sum at depth `N` whose largest index is `J` is a multiple of `2^−J` and
is below `2^(e+1)`, so it needs at most `e + 1 + J` significand bits and is exact
iff `J ≤ 52 − e`. While that holds every addition is exact, so **every** ordering
returns the exact sum. At `J = 53 − e` the top of the binade needs 54 bits; the
first rounding appears, and its direction depends on where in the sequence it
lands, i.e. on the ordering. The first term of group `J` is index `kJ`, so
N*(binary64) = k·(53 − e), N*(binary32) = k·(24 − e),
and the shift between precisions is `k·(53 − 24) = 29k`, independent of `e`.
The brief's own sketch (`2^−j` absorbed when `2^−j < ε·S`, `S ≈ 2k`, so
`j* = 52 − log2(2k)` and `N = k(52 − log2 2k)`) is a factor of four loose: the
absorption test is against **half** an ulp, `2^(e−53)`, not against `ε·S`. The
sketch is therefore 2 j-units, i.e. `2k` terms, early (3 terms when `k = 1`).
Both numbers are reported;
criterion 1 is evaluated against the derived index, and the offset from the
sketch is shown alongside so that the discrepancy is visible rather than hidden.
## Pre-specified support / refute criteria
Quoted verbatim from the brief.
> **SUPPORT for the phase transition** requires:
>
> 1. For every k, the switch from "all 200 orderings agree" to "some disagree" happens
> within 5 terms of the predicted index, in both precisions; and
> 2. the position of the switch moves by the predicted amount when precision changes
> (29 terms between binary64 and binary32 per factor of 2^29); and
> 3. the distribution of results above the threshold is discrete (a small number of
> distinct values, differing by one ulp), not a continuous spread.
>
> **REFUTE for the catastrophe claim** if, at fixed condition number, relative error is
> flat in depth (ratio between 10,000-term and 100-term error within 20%), while at fixed
> depth it grows with condition number.
### Operational details fixed in advance
* "The predicted index" is the derived `N* = k(53 − e)` above; the sketch's
`N = k(52 − log2 2k)` is reported as a second, clearly labelled column.
* Criterion 2 is checked as an equality `observed(binary64) − observed(binary32)
= 29k` for every `k`.
* Criterion 3 is operationalised as: at the threshold depth, at most 4 distinct
results among the 200 orderings; and at the threshold plus 16 terms, at most
12. "One ulp" is checked by expressing the spread in units in the last place.
* The refute criterion's "flat" is `0.80 ≤ ratio ≤ 1.20`, applied to the
replicate-mean relative error. It is informative only where the error is above
the ordinary rounding floor `u·√n` (a sum with no cancellation already shows
relative error of that size, and a ratio of two floor-level numbers measures
nothing). Which `κ` are above the floor is reported explicitly, and the clause
is evaluated both with and without that restriction.
* "Grows with condition number" is checked as a rise of more than 10⁶ between
`κ = 1` and `κ = 10⁹` at each depth.
## Decision rule
The two verdicts are independent.
* Reproducibility threshold **supported** iff criteria 1, 2 and 3 all hold;
otherwise **not supported as pre-specified**, item by item.
* Catastrophe **refuted** iff the refute criterion's two clauses hold above the
rounding floor; **supported** if relative error at fixed `κ` grows with depth
in the way the volume's claim requires; **inconclusive** otherwise.
No criterion was changed after the numbers were seen. Where a number lands the
"wrong" way (the offset from the brief's sketch, the two depths' ratios at
`κ = 1` and `κ = 10`) it is reported with the same prominence as the rest.