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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-11T11:36:18Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-11T11:42:38Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -172,8 +172,11 @@ static int d_compare(cell_t d1h, cell_t d1l, cell_t d2h, cell_t d2l) {
|
||||
if (d1h < d2h) return -1;
|
||||
if (d1h > d2h) return 1;
|
||||
|
||||
unsigned long ul1 = (unsigned long) d1l;
|
||||
unsigned long ul2 = (unsigned long) d2l;
|
||||
// ucell_t, not plain unsigned long -- see double_word_d_plus()'s
|
||||
// matching comment (unsigned long is only 32-bit on some bare-metal
|
||||
// aarch64 cross-compile targets).
|
||||
ucell_t ul1 = (ucell_t) d1l;
|
||||
ucell_t ul2 = (ucell_t) d2l;
|
||||
return (ul1 > ul2) - (ul1 < ul2);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user