Fix d_compare: use ucell_t instead of unsigned long (FABRIC-3.md §XII.4)
d_compare() (double_words.c), the static helper backing DMAX/DMIN/D</D=,
had the same bare-unsigned-long pattern already fixed in D+/D-/DNEGATE
(commit bea8d74) -- 32-bit unsigned long on this aarch64 target silently
truncating the low-cell comparison. Switched to ucell_t.
Verified with four cases designed specifically to expose the old
truncation: two doubles sharing the same high cell but with low cells
differing only in bits 32-63 (invisible to a 32-bit-truncated compare,
e.g. 2^32 vs 0):
4294967296 0 0 0 D= . -> 0 (correctly not equal)
4294967296 0 0 0 DMIN SWAP D. -> 0 (correctly picks the smaller)
0 0 4294967296 0 D< . -> -1 (correct)
4294967296 0 0 0 D< . -> 0 (correct)
All four pass identically on amd64, aarch64, and riscv64. These cases
were never run before this fix -- the exerciser campaign never touched
DMAX/DMIN/D</D= at all, so this is the first real evidence d_compare()
was ever exercised on any architecture. Full 24-case exerciser also
reran clean on all three architectures (no regression).
D2*/D2/ use unsigned long long (C-standard-guaranteed >=64-bit) and were
never part of this bug class. All four affected words (D+, D-, DNEGATE,
d_compare) are now fixed; M*'s separate universal DOUBLE-OVERFLOW bug
remains open (unrelated defect, out of scope here).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bea8d7436a
commit
1a716c8048
+21
-8
@@ -2142,8 +2142,8 @@ against the other two architectures once the campaign completes; not yet root-ca
|
||||
as a bug on its own.
|
||||
|
||||
### XII.4 — Campaign completed: full 27-leg run (9 identities × 3 architectures), two real
|
||||
FORTH-79 engine bugs found — bug 2 (`D+`/`D-`/`DNEGATE`) root-caused and FIXED 2026-09-11;
|
||||
bug 1 (`M*`) still open, report only (2026-09-10, after §XIII's WIREBIND fix)
|
||||
FORTH-79 engine bugs found — bug 2 (`D+`/`D-`/`DNEGATE`/`d_compare`) root-caused and FIXED
|
||||
2026-09-11; bug 1 (`M*`) still open, report only (2026-09-10, after §XIII's WIREBIND fix)
|
||||
|
||||
All 27 legs run: `zuse` (auto-attached, exercised directly on Hera's own console) plus `rajames`
|
||||
(the `bob` thumbdrive's actual registered identity -- see the naming-mismatch note below) and
|
||||
@@ -2236,13 +2236,26 @@ project's standing rule (report, don't fix without being asked):**
|
||||
`D+`). `M*`'s universal failure (bug 1, below) is untouched -- unrelated defect, `D+`/`D-`/
|
||||
`DNEGATE`-specific fix only.
|
||||
|
||||
**Still open, not fixed (out of the scope actually requested):** the `d_compare()` helper
|
||||
(used by `DMAX`/`DMIN`/`D<`/`D=`) uses the same bare-`unsigned long` pattern
|
||||
(`double_words.c` lines ~170-171) and is equally vulnerable in principle -- never exercised by
|
||||
`std79-exerciser.fth`, not fixed here since the fix request named `D+`/`D-`/`DNEGATE`
|
||||
specifically. `double_word_d_two_star()`/`double_word_d_two_slash()` (`D2*`/`D2/`) use
|
||||
**`d_compare()` fixed too, 2026-09-11** (on explicit follow-up request "fix d_compare too"):
|
||||
the helper backing `DMAX`/`DMIN`/`D<`/`D=` (`double_words.c` lines ~170-171) had the identical
|
||||
bare-`unsigned long` pattern, never exercised by `std79-exerciser.fth` itself. Switched to
|
||||
`ucell_t`, same as the other three. Verified with four cases designed specifically to expose
|
||||
the old 32-bit-truncation bug -- two doubles sharing the same high cell but with low cells
|
||||
differing only in bits 32-63 (invisible to a 32-bit-truncated compare, e.g. `2^32` vs `0`):
|
||||
`4294967296 0 0 0 D= .` → `0` (correctly *not* equal; the old bug would truncate both low
|
||||
cells to `0` and wrongly report equal); `4294967296 0 0 0 DMIN SWAP D.` → `0` (correctly
|
||||
picks the smaller double's low cell; the old bug's false "equal" tie-break would have
|
||||
returned the wrong operand for this ordering); `0 0 4294967296 0 D< .` → `-1` and
|
||||
`4294967296 0 0 0 D< .` → `0` (both directions of the same comparison, both correct). All
|
||||
four pass identically on amd64, aarch64, and riscv64 -- confirms the fix and, since these
|
||||
cases were never run before this fix, is the first real evidence `d_compare()` was ever
|
||||
correct or incorrect on any architecture (the campaign itself never touched `DMAX`/`DMIN`/
|
||||
`D<`/`D=` at all). `double_word_d_two_star()`/`double_word_d_two_slash()` (`D2*`/`D2/`) use
|
||||
`unsigned long long` instead, which the C standard guarantees is at least 64-bit everywhere --
|
||||
not part of this bug class, nothing to fix there.
|
||||
were never part of this bug class, nothing to fix there.
|
||||
|
||||
**All four affected words (`D+`, `D-`, `DNEGATE`, `d_compare`) are now closed.** The only
|
||||
remaining open item from this campaign is bug 1 (`M*`, below) -- a separate, unrelated defect.
|
||||
|
||||
`M*`'s universal failure (bug 1) is a separate, unrelated defect (present on every architecture,
|
||||
so not a `long`-width issue) and would need its own trace starting from `M*`'s own double-cell
|
||||
|
||||
Reference in New Issue
Block a user