test(v4.0.0): execute the in-line constants Q.1, Q.0 and Q.SCALE
DECOMPOSITION.md 5.26 gives them fate IN: the compiler places two literals, low cell first. Q.1 and Q.SCALE are `65536 0`, Q.0 is `0 0`; v3's are 65536, 0 and 65536. Each expansion is assembled and run, and checked against v3's value; then in use: Q.1 Q.TO-INT is 1, 1 Q.FROM-INT is Q.1, and q Q.1 Q.* and q Q.0 Q.+ return q for 2000 pseudo-random q plus 0, Q max and Q min. At 32- and 64-bit cells, optimised and ASan+UBSan (`make test`, `make sanitize`). A wrong value in any of the three fails. Test results on the amd64 host only. This is a development check, not acceptance (JUSTIFICATION.md section 16). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8235078b3b
commit
13ec4b1b6c
@@ -676,7 +676,7 @@ word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values
|
||||
| `(QE)` | CAP | Variable, 8 cells: `Q.EXP`'s `x`, term, sum, sign and term index. |
|
||||
| `Q.FROM-INT` | CAP | `push 0 a! 0 pop 15 FOR +* UNEXT push drop a pop` — `n * 2^16` as a signed double. `+*` with `S = 0` never adds, so each step is an exact arithmetic right shift of `T:A`; starting from `T:A = n:0` (that is, `n * 2^N`) and shifting `N-16` bits leaves `n * 2^16`. The count is `N-17`: 15 at 32-bit cells, 47 at 64. Negative values are no longer clamped to 0. Executed on the golden model (2026-10-02): matches v3's `q48_from_u64` for `n >= 0` (at 64-bit cells in the low cell, where v3 wraps for `n >= 2^47`, per D-10). Clobbers `A`. |
|
||||
| `Q.TO-INT` | CAP | `push a! 0 pop 15 FOR +* UNEXT drop drop a` — the same `+*` shift, 16 bits at every cell width, leaving the low cell in `A`. Rounds toward minus infinity, as v3's arithmetic shift does (−1.5 gives −2). Executed on the golden model (2026-10-02): v3's `q48_to_u64` exactly at 64-bit cells, its low cell at 32. Clobbers `A`. |
|
||||
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants. |
|
||||
| `Q.1` `Q.0` `Q.SCALE` | IN | Double-cell constants, placed in line as two literals, low cell first: `Q.1` and `Q.SCALE` are `65536 0` (1.0), `Q.0` is `0 0`. v3's are 65536, 0 and 65536. Executed on the golden model (2026-10-03): the values match v3's, `Q.1 Q.TO-INT` is 1, `1 Q.FROM-INT` is `Q.1`, and `q Q.1 Q.*` and `q Q.0 Q.+` return `q`. |
|
||||
| `Q.=` `Q.<` `Q.>` `Q.0=` `Q.MAX` `Q.MIN` | CAP | `D=`, `D<`, `2SWAP D<` (for `Q.>`), `D0=`, `DMAX`, `DMIN`. Signed (D-8); v3 compared unsigned, so v3 agrees only when both values have the same sign. Executed on the golden model (2026-10-02). `Q.>` was given here as `SWAP D<`, which swaps single cells, not Q values, and gave a wrong answer in 11702 of 20169 test cases. |
|
||||
| `Q.PRINT` | CAP | Pictured output, five fractional digits. |
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
* for 0 with NODE-ERROR at or below zero.
|
||||
* (Q.REDUCE), Q.SIN and Q.COS are checked bit for bit against v3's
|
||||
* q48_reduce_angle, q48_sin_approx and q48_cos_approx on angles of every
|
||||
* size and sign. Every word is
|
||||
* size and sign. The in-line constants Q.1, Q.0 and Q.SCALE are checked
|
||||
* against v3's values and in use. Every word is
|
||||
* also probed for how much of the 10- and 9-deep circular stacks (D-2) it
|
||||
* leaves to its caller.
|
||||
*
|
||||
@@ -79,7 +80,8 @@ static v4_cell w_nip, w_swap, w_or, w_negate, w_rot, w_zless, w_zequal,
|
||||
w_slashmod, w_mplus, w_dminus, w_d0equal, w_dequal,
|
||||
w_qfromint, w_qtoint, w_less, w_equal, w_dless, w_2swap, w_2over,
|
||||
w_dmax, w_dmin, w_qgt_doc, w_qgt, w_dltkeep, w_qstar, w_d2starc, w_uqdiv, w_qslash, w_qexp, w_qsqrt, w_qlog,
|
||||
w_qreduce, w_qsin, l_trig, w_qcos;
|
||||
w_qreduce, w_qsin, l_trig, w_qcos,
|
||||
w_q1, w_q0, w_qscale, w_q1_times, w_q0_plus, w_q1_toint, w_one_fromint;
|
||||
|
||||
#define O(name) v4_asm_op(&as, V4_OP_##name)
|
||||
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
|
||||
@@ -1301,6 +1303,28 @@ static void build(void)
|
||||
#undef HERE_
|
||||
#undef FWD
|
||||
|
||||
/* Q.1, Q.0, Q.SCALE 5.26, fate IN
|
||||
* In-line double-cell constants: the compiler places two literals, low
|
||||
* cell first. Q.1 and Q.SCALE are `65536 0` (1.0), Q.0 is `0 0`; v3's
|
||||
* are 65536, 0 and 65536. Each expansion is assembled here followed by
|
||||
* `;` so that it can be run, and then in the middle of a definition, as
|
||||
* it would be used:
|
||||
* Q.1 Q.* ( q -- q ) Q.0 Q.+ ( q -- q )
|
||||
* Q.1 Q.TO-INT ( -- 1 ) 1 Q.FROM-INT ( -- q ), which must be Q.1 */
|
||||
#define Q_ONE() do { LIT(65536); LIT(0); } while (0)
|
||||
#define Q_ZERO() do { LIT(0); LIT(0); } while (0)
|
||||
#define Q_SCALE() do { LIT(65536); LIT(0); } while (0)
|
||||
w_q1 = v4_asm_label(&as); Q_ONE(); O(SEMI);
|
||||
w_q0 = v4_asm_label(&as); Q_ZERO(); O(SEMI);
|
||||
w_qscale = v4_asm_label(&as); Q_SCALE(); O(SEMI);
|
||||
w_q1_times = v4_asm_label(&as); Q_ONE(); CALL(w_qstar); O(SEMI);
|
||||
w_q0_plus = v4_asm_label(&as); Q_ZERO(); CALL(w_dplus); O(SEMI);
|
||||
w_q1_toint = v4_asm_label(&as); Q_ONE(); CALL(w_qtoint); O(SEMI);
|
||||
w_one_fromint = v4_asm_label(&as); LIT(1); CALL(w_qfromint); O(SEMI);
|
||||
#undef Q_SCALE
|
||||
#undef Q_ZERO
|
||||
#undef Q_ONE
|
||||
|
||||
CHECK(v4_asm_ok(&as), "foundation words assemble");
|
||||
}
|
||||
|
||||
@@ -2540,6 +2564,31 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Q.1, Q.0 and Q.SCALE: v3's values, and how they behave in use. */
|
||||
{
|
||||
v4_cell ol, oh, zl, zh;
|
||||
uint64_t x = 0x8EBC6AF09C88C6E3u;
|
||||
qcells(65536u, &ol, &oh); /* v3 Q.1 and Q.SCALE */
|
||||
qcells(0u, &zl, &zh); /* v3 Q.0 */
|
||||
CHECK(call(w_q1, 0, 0, 0, 0) && left2(ol, oh), "Q.1 is v3's 65536");
|
||||
CHECK(call(w_q0, 0, 0, 0, 0) && left2(zl, zh), "Q.0 is v3's 0");
|
||||
CHECK(call(w_qscale, 0, 0, 0, 0) && left2(ol, oh), "Q.SCALE is v3's 65536");
|
||||
CHECK(call(w_q1_toint, 0, 0, 0, 0) && left1(1), "Q.1 Q.TO-INT is 1");
|
||||
CHECK(call(w_one_fromint, 0, 0, 0, 0) && left2(ol, oh), "1 Q.FROM-INT is Q.1");
|
||||
for (unsigned i = 0; i < 2000; i++) {
|
||||
uint64_t q;
|
||||
v4_cell ql, qh;
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
|
||||
q = asr64(x, (unsigned)(x & 63u));
|
||||
if (i == 0) q = 0;
|
||||
if (i == 1) q = ((uint64_t)1 << 63) - 1u;
|
||||
if (i == 2) q = (uint64_t)1 << 63;
|
||||
qcells(q, &ql, &qh);
|
||||
CHECK(call(w_q1_times, 2, ql, qh, 0) && left2(ql, qh), "q Q.1 Q.* is q [%u]", i);
|
||||
CHECK(call(w_q0_plus, 2, ql, qh, 0) && left2(ql, qh), "q Q.0 Q.+ is q [%u]", i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Q./ at the overflow boundary: |a| * 2^16 against |b| * 2^(2N-1), for
|
||||
* divisors around 2^16 and 2^17, every sign. */
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user