sots-re/verify/results/fpu-cw
alex 849c5069fc lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed
Seven End Turns from ref-turn2.sav on VM140, six control words, whole-state
checksum on every post-turn autosave.

53-bit and 64-bit x87 give byte-identical state across all 35,394 leaves, so
an x64/SSE port computing in IEEE double has NO double-rounding budget to
preserve and floats=bits is free. Two settings do move state, each reproduced
on a repeat run:

  0x007f (24-bit)   Sys[112 "Gamma Cephei"]/Pop2/PopG/PopC 540000000 -> 540000002
  0x1a7f (round-up) Flt[34 "Beta Fleet"]/Pos/.[0] and /Pos/.[2], 1 ULP each

So the port must hold intermediates at 53 bits and use round-to-nearest --
both SSE defaults, now measured rather than assumed, each with a named
regression witness.

The briefed triple was under-powered: 0x027f is 53-bit (it differs from 0x127f
only in bit 12, infinity control, ignored since the 387) and 0x137f is 64-bit,
not a rounding change. Run as written all three come back identical, and that
would have "proved" something false on both axes that matter.

Evidence the forced word actually held: read-back at each force site plus 38
independent in-pipeline hook samples per run spanning turn phases 4, 6 and 8,
all reading the forced value. Mars::Application::Run calls
_controlfp(0x50000,0x3070300) at 0x0089f606 every frame, which is 0x127f, so
forcing at StrategyClient::EndTurn is wiped before the turn runs;
StrategyServer::BeginProcessTurn is the point that works.

Also re-confirms the End-Turn determinism oracle on engine cef889e:
bb4fd9ac... / 978041ac... unchanged.

New tools: verify/fpu-cw/cw_census.py, verify/fpu-cw/trace_bitdiff.py (the
latter exists because under a forced 24-bit word the CRT's own %g rendering
degrades, so trace text is not a valid comparison surface).
2026-09-08 05:07:53 -04:00
..
run-1a7f lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-1a7f-rep lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-007f lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-007f-rep lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-027f lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-127f lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-137f lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
run-off lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
cw-census.txt lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
README.md lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
state-checksum-diffs.txt lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00
trace-bitdiff.txt lane F: x87 precision sensitivity measured; STATE_CHECKSUM 3.5 closed 2026-09-08 05:07:53 -04:00

x87 precision-sensitivity experiment — artefacts (lane F, 2026-09-08)

Read findings/subsystems/fpu-precision-sensitivity.md for the result and the argument. This directory is the evidence.

Seven End-Turn runs from C:\SOTS\SavedGames\ref-turn2.sav (ab4ac2d7…) on VM140, shim build fpucw-cef889e-20260908T0803Z. The hook set, trace path and flush policy are identical in every run; the ONLY difference between the shim.cfg files is the fpu.force= line.

run-off/       nothing forced -- oracle re-confirmation + the game's own control-word timeline
run-027f/      fpu.force=0x027f   53-bit, nearest   (the MSVC CRT default; NOT 24-bit)
run-127f/      fpu.force=0x127f   53-bit, nearest   -- the CONTROL: what the game sets itself
run-137f/      fpu.force=0x137f   64-bit extended, nearest
run-007f/      fpu.force=0x007f   24-bit single, nearest
run-1a7f/      fpu.force=0x1a7f   53-bit, round-toward-+infinity
run-007f-rep/  repeat of run-007f -- reproducibility of the divergence
run-1a7f-rep/  repeat of run-1a7f

Each run directory holds (Autosave).sav (post-turn, the thing under test), (Autosave EndTurn).sav (pre-turn, the run-to-run control -- bb4fd9ac… in every run), shim.log (the force / read-back / per-tick-sampler timeline), shim.cfg and the gzipped trace. The two -rep runs keep only the saves and the log.

Derived, regenerate with the commands below:

  • cw-census.txt — the fpu_cw every hooked call observed, per run. This is the evidence that the forced word held for the duration of the turn: 38 samples per run spanning turn phases 4, 6 and 8, all reading the forced value.
  • state-checksum-diffs.txt — every run's post-turn autosave against run-127f.
  • trace-bitdiff.txt — bit-exact trace comparison. Note the trap it exists for: under a forced 24-bit control word the CRT's own %g rendering degrades, so trace text is not a valid comparison surface.
uv run python3 verify/fpu-cw/cw_census.py verify/results/fpu-cw/run-*/shim.trace.jsonl.gz
uv run python3 verify/state-checksum/state_checksum.py \
    "verify/results/fpu-cw/run-127f/(Autosave).sav" "verify/results/fpu-cw/run-007f/(Autosave).sav"
uv run python3 verify/fpu-cw/trace_bitdiff.py \
    verify/results/fpu-cw/run-127f/shim.trace.jsonl.gz verify/results/fpu-cw/run-1a7f/shim.trace.jsonl.gz

Headline hashes (sha256 of the file, gzip container included -- MTIME is 0, so this is valid):

bb4fd9ac89f41e3bc0db2af08b18ce83417521ac4bcee695fc9fa6ce16e30948  (Autosave EndTurn).sav  ALL SEVEN RUNS
978041acd168b56ed8eb3f5e42e78d5e70eae6e6517d75e659a5eb7ca3d60921  (Autosave).sav  stock / 0x027f / 0x127f / 0x137f
ba2435beb17265af3abddb58cd26e926d42b39b358ef6d0c76f9897386174918  (Autosave).sav  0x007f  (both runs)
e48e25fa8425a8a1d174efe3e8a553153447f11f8f91c5fda25bd7db3a26cca7  (Autosave).sav  0x1a7f  (both runs)

The first two lines are also the determinism-oracle re-confirmation on engine cef889e (lane M's movement fix included): unchanged, as it must be — that fix touches ours, never the original.