feat(v4.0.0): Q.LOG, flattened, bit for bit with v3
Q.LOG is v3's q48_log_approx: x = 2^k * m with 1.0 <= m < 2.0, ln m by up to 6 Newton rounds on e^y = m, result y + k * ln 2. As ruled, e^y is Q.EXP's Taylor loop written in line rather than a call to Q.EXP, so Q.LOG calls only Q.*, Q./ and UM*. After the reduction m, y, the Taylor term and its sum each fit one cell, so the 7-cell variable (QL) holds single cells. x <= 0 returns 0 and sets NODE-ERROR (D-12). Checked bit for bit against v3's q48_log_approx (ported into the test) on 22 positive edge values, 2000 pseudo-random x of every magnitude and a sweep of every 17th reduced m, at 32- and 64-bit cells, optimised and ASan+UBSan (`make test`, `make sanitize`). Mutating the round count, the 100-ulp stop, ln 2 or the Taylor length all fail. v3's clamp y = max(y - corr, 0) is kept but cannot fire: a scan of all 65536 values of m in C never reaches it, and never needs more than 4 rounds. So no test covers it. The test harness's per-call step limit is raised from 100000 to 4000000 instruction words: a 64-bit Q./ takes about 6500, and a mutant forcing extra Newton rounds ran past the old limit and looked like a width difference. Headroom (data under arg / return): Q.LOG 3/2. 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
a2a4a85d38
commit
e117333a87
@@ -147,8 +147,8 @@ IF body1 ELSE body2 THEN → if L1 drop body1 jump L2
|
||||
times, as on the F18. `FOR ... UNEXT` is the same but the body must fit in one instruction word.
|
||||
|
||||
**Register conventions.** `A` and `B` are caller-saved. A word that uses them says so. Words in this
|
||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT SEND RECV`. Words that clobber `B`:
|
||||
`Q./ Q.EXP Q.SQRT SEND RECV`.
|
||||
document that clobber `A`: `@ ! +! -! 2@ 2! C@ C! UM* * UM/MOD Q.FROM-INT Q.TO-INT Q.* Q./ Q.EXP Q.SQRT Q.LOG SEND RECV`. Words that clobber `B`:
|
||||
`Q./ Q.EXP Q.SQRT Q.LOG SEND RECV`.
|
||||
|
||||
**Return-stack words** (`>R R> R@ 2>R 2R> 2R@ I J UNLOOP` and the loop runtimes) are always IN.
|
||||
|
||||
@@ -664,7 +664,9 @@ word. Per D-10 it occupies two cells on a 64-bit node too. Per D-8, v4 Q values
|
||||
| `D2*C` | CAP | `( lo hi cin -- lo' hi' cout )`: the double shifted left one bit, `cin` entering at the bottom and `cout` the bit leaving the top (both 0 or 1). `a! dup -if L1 drop 1 jump L2 L1: drop 0 L2: push over -if L3 drop 1 jump L4 L3: drop 0 L4: over + + push 2* a + pop pop` — `cin` waits in `A`, and `2hi + m` is `over + +`, so it needs one return entry. Executed on the golden model (2026-10-02). Clobbers `A`. |
|
||||
| `(Q/)` | CAP | Variable, 5 cells: `Q./`'s quotient register, divisor and result sign. Like `BASE` and the hold buffer, it lives in node memory. |
|
||||
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` — executed on the golden model (2026-10-02) against v3's `q48_abs` and `0 - q`: bit-for-bit at 32-bit cells, including v3's wrap of Q min to itself; at 64-bit cells the low cell is v3's and the high cell is the true sign, per D-10. |
|
||||
| `Q.LOG` `Q.SIN` `Q.COS` | CAP | Algorithms ported from `q48_words.c`; the hosted C versions are the golden model. |
|
||||
| `Q.SIN` `Q.COS` | CAP | Algorithms ported from `q48_words.c`; the hosted C versions are the golden model. |
|
||||
| `Q.LOG` | CAP | v3's `q48_log_approx`: `x = 2^k * m` with `1.0 <= m < 2.0`; `ln m` by up to 6 Newton rounds on `e^y = m` from `y = m - 1.0`, stopping once `|m - e^y| < 100` ulp; result `y + k * ln 2` with `ln 2 = 45426`. `e^y` is `Q.EXP`'s Taylor loop **written in line** (flattened, ruled 2026-10-02), so `Q.LOG` calls only `Q.*`, `Q./` and `UM*` and the call chain is one level shorter than through `Q.EXP`. After the reduction `m`, `y`, the Taylor term and its sum each fit one cell at either width, so the variable `(QL)` holds single cells. `x <= 0` returns 0 and sets `NODE-ERROR` (D-12). Executed on the golden model (2026-10-03): bit for bit v3's result for every `x > 0` tested, including a sweep of the reduced `m`. v3's clamp `y = max(y - corr, 0)` is kept for fidelity but cannot fire: over all 65536 values of `m` it is never reached, and the iteration never needs more than 4 rounds. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(QL)` | CAP | Variable, 7 cells: `Q.LOG`'s `m`, `y`, `k`, rounds left, Taylor term (then `delta`), sum, and `n * 1.0`. |
|
||||
| `Q.SQRT` | CAP | v3's `q48_sqrt_approx`: Newton from `x0 = q/2 + 0.25`, up to 8 rounds of `x' = (x + q/x) / 2`, returning `x` as soon as `|x' - x| < 10` ulp; `sqrt(0) = 0`, `sqrt(1.0) = 1.0`. `q < 0` returns 0 and sets `NODE-ERROR` (D-12). `q`, `x` and the rounds left live in the variable `(QR)`. The halving is a logical double shift, `push a! 0 pop +* MAXHI and push drop a pop`. Executed on the golden model (2026-10-02): bit for bit v3's result for `0 <= q < 2^48`; above that v3's `q48_div` saturates and v4 divides correctly, so they part. Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
| `(QR)` | CAP | Variable, 5 cells: `Q.SQRT`'s `q`, `x` and rounds left. |
|
||||
| `Q.EXP` | CAP | v3's `q48_exp_approx`: `1 + x + x^2/2! + …` to 10 terms on `x = |q|`, stopping once a term is below 50 ulp; `1.0 Q./ e^|q|` for `q < 0`; `e^0 = 1.0`; `|q| >= 16.0` gives 0 (`q < 0`) or **Q max** (v3 returned all ones, which reads as −ulp under D-8). `x`, the term, the sum, the sign and the term index live in the variable `(QE)`, so only `Q.*` and `Q./` arguments sit on the stacks. Executed on the golden model (2026-10-02): bit for bit v3's result for `|q| < 16.0` (at 64-bit cells as the same value, high cell 0). Leaves its caller 3 data cells under its argument and 2 return entries. Clobbers `A` and `B`. |
|
||||
|
||||
+239
-6
@@ -29,7 +29,9 @@
|
||||
* values, and against v3's q48_div where v4 keeps v3's meaning.
|
||||
* Q.EXP is checked bit for bit against v3's q48_exp_approx for |q| < 16.0.
|
||||
* Q.SQRT is checked bit for bit against v3's q48_sqrt_approx for
|
||||
* 0 <= q < 2^48, and for 0 with NODE-ERROR below zero (D-12). Every word is
|
||||
* 0 <= q < 2^48, and for 0 with NODE-ERROR below zero (D-12).
|
||||
* Q.LOG is checked bit for bit against v3's q48_log_approx for x > 0, and
|
||||
* for 0 with NODE-ERROR at or below zero. Every word is
|
||||
* also probed for how much of the 10- and 9-deep circular stacks (D-2) it
|
||||
* leaves to its caller.
|
||||
*
|
||||
@@ -56,6 +58,8 @@ static int failures = 0, checks = 0;
|
||||
#define QE ((v4_cell)(V4_NODE_WORDS - 16u))
|
||||
/* (QR), Q.SQRT's 5-cell variable: q, x (two cells each), rounds left. */
|
||||
#define QR ((v4_cell)(V4_NODE_WORDS - 24u))
|
||||
/* (QL), Q.LOG's 7-cell variable: m, y, k, rounds left, term, sum, n*1.0. */
|
||||
#define QL ((v4_cell)(V4_NODE_WORDS - 32u))
|
||||
#define MAXU ((v4_ucell)~(v4_ucell)0)
|
||||
#define FLAG(c) ((c) ? V4_ALL_ONES : (v4_cell)0)
|
||||
|
||||
@@ -69,7 +73,7 @@ static v4_cell w_nip, w_swap, w_or, w_negate, w_rot, w_zless, w_zequal,
|
||||
w_ugreater, w_abs, w_s2d, w_dplus, w_dnegate, w_dabs, w_smrem,
|
||||
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_dmax, w_dmin, w_qgt_doc, w_qgt, w_dltkeep, w_qstar, w_d2starc, w_uqdiv, w_qslash, w_qexp, w_qsqrt, w_qlog;
|
||||
|
||||
#define O(name) v4_asm_op(&as, V4_OP_##name)
|
||||
#define LIT(v) v4_asm_lit(&as, (v4_cell)(v))
|
||||
@@ -1018,6 +1022,156 @@ static void build(void)
|
||||
#undef FETCH2
|
||||
#undef STORE2
|
||||
#undef HERE_
|
||||
#undef FWD
|
||||
|
||||
/* : Q.LOG ( x -- ln x ) 5.26, v3's q48_log_approx
|
||||
* x = 2^k * m with 1.0 <= m < 2.0; ln m by up to 6 Newton rounds on
|
||||
* e^y = m from y = m - 1.0; result y + k * ln 2 (ln 2 = 45426). e^y is
|
||||
* Q.EXP's Taylor loop written here in line, so Q.LOG calls only Q.*, Q./
|
||||
* and UM*. After the reduction m, y, the Taylor term and sum all fit one
|
||||
* cell, so (QL) holds single cells: m y k rounds term sum n*1.0.
|
||||
* x <= 0 gives 0 and sets NODE-ERROR (D-12). D2/ is the logical double
|
||||
* shift right, as in Q.SQRT.
|
||||
* dup -if POS drop jump ERR
|
||||
* POS: drop over over OR if ZERO drop 0 QL 2 + b! !b
|
||||
* DOWN: dup if CHKLO drop jump SHR
|
||||
* CHKLO: drop over -131072 and if DOWNDONE drop
|
||||
* SHR: D2/ QL 2 + b! @b 1 + !b jump DOWN x >= 2.0: halve, k+1
|
||||
* DOWNDONE: drop drop m
|
||||
* UP: dup -65536 and if UPSHIFT drop jump NORM
|
||||
* UPSHIFT: drop 2* QL 2 + b! @b -1 + !b jump UP m < 1.0: double, k-1
|
||||
* NORM: dup QL b! !b -65536 + QL 1 + b! !b 6 QL 3 + b! !b
|
||||
* NEWTON: QL 1 + b! @b dup QL 4 + b! !b 65536 + QL 5 + b! !b
|
||||
* 131072 QL 6 + b! !b
|
||||
* EXPL: QL 4 + b! @b 0 QL 1 + b! @b 0 Q.* drop
|
||||
* 0 QL 6 + b! @b 0 Q./ drop dup QL 4 + b! !b
|
||||
* dup QL 5 + b! @b + !b
|
||||
* -50 + -if EXPC drop jump EXPD
|
||||
* EXPC: drop QL 6 + b! @b 65536 + dup !b -720896 + -if EXPD2 drop jump EXPL
|
||||
* EXPD2: drop
|
||||
* EXPD: QL b! @b QL 5 + b! @b push inv pop + inv delta = m - e^y
|
||||
* dup QL 4 + b! !b delta waits in the term's cell
|
||||
* if DZ -if DPOS
|
||||
* inv 1 + 0 QL 5 + b! @b 0 Q./ drop corr = -delta / e^y
|
||||
* QL 1 + b! @b SWAP push inv pop + inv -if KEEP drop 0 KEEP: QL 1 + b! !b
|
||||
* jump TEST y = max(y - corr, 0)
|
||||
* DPOS: 0 QL 5 + b! @b 0 Q./ drop QL 1 + b! @b + !b jump TEST
|
||||
* DZ: drop
|
||||
* TEST: QL 4 + b! @b -if AP inv 1 + AP: -100 + -if CONT drop jump FIN
|
||||
* CONT: drop QL 3 + b! @b -1 + dup !b if FIN0 drop jump NEWTON
|
||||
* FIN0: drop
|
||||
* FIN: QL 1 + b! @b QL 2 + b! @b
|
||||
* -if KP inv 1 + 45426 UM* drop inv 1 + jump KS KP: 45426 UM* drop
|
||||
* KS: + dup -if SP drop -1 ; SP: drop 0 ;
|
||||
* ZERO: drop ERR: drop drop NODE-ERROR b! -1 !b 0 0 ;
|
||||
* Clobbers A and B. */
|
||||
#define FWD(op) v4_asm_branch_fwd(&as, V4_OP_##op)
|
||||
#define HERE_(r) v4_asm_resolve(&as, (r), v4_asm_label(&as))
|
||||
#define JUMP_TO(l) v4_asm_branch(&as, V4_OP_JUMP, (l))
|
||||
#define VGET(a) do { LIT(a); O(BANG_B); O(FETCH_B); } while (0)
|
||||
#define VSET(a) do { LIT(a); O(BANG_B); O(STORE_B); } while (0)
|
||||
#define SWAP_INLINE() do { O(OVER); O(PUSH); O(PUSH); O(DROP); O(RPOP); O(RPOP); } while (0)
|
||||
#define SUB_INLINE() do { O(PUSH); O(INV); O(RPOP); O(ADD); O(INV); } while (0)
|
||||
#define D2SLASH() do { O(PUSH); O(BANG_A); LIT(0); O(RPOP); O(MUL_STEP); \
|
||||
LIT((v4_cell)(MAXU >> 1)); O(AND); O(PUSH); O(DROP); O(PUSH_A); O(RPOP); } while (0)
|
||||
{
|
||||
v4_asm_ref pos, j_err, zero, chklo, j_shr, downdone, upshift, j_norm, expc, j_expd,
|
||||
expd2, dzero, dpos, keep, j_test, ap, contn, j_fin, fin0, kp, j_ks, sp;
|
||||
v4_cell l_down, l_up, l_newton, l_expl, l_test, l_err;
|
||||
|
||||
w_qlog = v4_asm_label(&as);
|
||||
O(DUP); pos = FWD(MINUS_IF);
|
||||
O(DROP); j_err = FWD(JUMP);
|
||||
HERE_(pos);
|
||||
O(DROP); O(OVER); O(OVER); CALL(w_or); zero = FWD(IF);
|
||||
O(DROP); LIT(0); VSET(QL + 2);
|
||||
l_down = v4_asm_label(&as);
|
||||
O(DUP); chklo = FWD(IF);
|
||||
O(DROP); j_shr = FWD(JUMP);
|
||||
HERE_(chklo);
|
||||
O(DROP); O(OVER); LIT(-131072); O(AND); downdone = FWD(IF);
|
||||
O(DROP);
|
||||
HERE_(j_shr);
|
||||
D2SLASH(); VGET(QL + 2); LIT(1); O(ADD); O(STORE_B); JUMP_TO(l_down);
|
||||
HERE_(downdone);
|
||||
O(DROP); O(DROP);
|
||||
l_up = v4_asm_label(&as);
|
||||
O(DUP); LIT(-65536); O(AND); upshift = FWD(IF);
|
||||
O(DROP); j_norm = FWD(JUMP);
|
||||
HERE_(upshift);
|
||||
O(DROP); O(TWO_STAR); VGET(QL + 2); LIT(-1); O(ADD); O(STORE_B); JUMP_TO(l_up);
|
||||
HERE_(j_norm);
|
||||
O(DUP); VSET(QL); LIT(-65536); O(ADD); VSET(QL + 1); LIT(6); VSET(QL + 3);
|
||||
l_newton = v4_asm_label(&as);
|
||||
VGET(QL + 1); O(DUP); VSET(QL + 4); LIT(65536); O(ADD); VSET(QL + 5);
|
||||
LIT(131072); VSET(QL + 6);
|
||||
l_expl = v4_asm_label(&as);
|
||||
VGET(QL + 4); LIT(0); VGET(QL + 1); LIT(0); CALL(w_qstar); O(DROP);
|
||||
LIT(0); VGET(QL + 6); LIT(0); CALL(w_qslash); O(DROP);
|
||||
O(DUP); VSET(QL + 4);
|
||||
O(DUP); VGET(QL + 5); O(ADD); O(STORE_B);
|
||||
LIT(-50); O(ADD); expc = FWD(MINUS_IF);
|
||||
O(DROP); j_expd = FWD(JUMP);
|
||||
HERE_(expc);
|
||||
O(DROP); VGET(QL + 6); LIT(65536); O(ADD); O(DUP); O(STORE_B);
|
||||
LIT(-720896); O(ADD); expd2 = FWD(MINUS_IF);
|
||||
O(DROP); JUMP_TO(l_expl);
|
||||
HERE_(expd2);
|
||||
O(DROP);
|
||||
HERE_(j_expd);
|
||||
VGET(QL); VGET(QL + 5); SUB_INLINE(); O(DUP); VSET(QL + 4);
|
||||
dzero = FWD(IF);
|
||||
dpos = FWD(MINUS_IF);
|
||||
O(INV); LIT(1); O(ADD); LIT(0); VGET(QL + 5); LIT(0); CALL(w_qslash); O(DROP);
|
||||
VGET(QL + 1); SWAP_INLINE(); SUB_INLINE();
|
||||
keep = FWD(MINUS_IF); O(DROP); LIT(0); HERE_(keep);
|
||||
VSET(QL + 1); j_test = FWD(JUMP);
|
||||
HERE_(dpos);
|
||||
LIT(0); VGET(QL + 5); LIT(0); CALL(w_qslash); O(DROP);
|
||||
VGET(QL + 1); O(ADD); O(STORE_B);
|
||||
{
|
||||
v4_asm_ref j_test2 = FWD(JUMP);
|
||||
HERE_(dzero);
|
||||
O(DROP);
|
||||
l_test = v4_asm_label(&as);
|
||||
v4_asm_resolve(&as, j_test2, l_test);
|
||||
}
|
||||
VGET(QL + 4);
|
||||
ap = FWD(MINUS_IF); O(INV); LIT(1); O(ADD); HERE_(ap);
|
||||
LIT(-100); O(ADD); contn = FWD(MINUS_IF);
|
||||
O(DROP); j_fin = FWD(JUMP);
|
||||
HERE_(contn);
|
||||
O(DROP); VGET(QL + 3); LIT(-1); O(ADD); O(DUP); O(STORE_B);
|
||||
fin0 = FWD(IF);
|
||||
O(DROP); JUMP_TO(l_newton);
|
||||
HERE_(fin0);
|
||||
O(DROP);
|
||||
HERE_(j_fin);
|
||||
VGET(QL + 1); VGET(QL + 2);
|
||||
kp = FWD(MINUS_IF);
|
||||
O(INV); LIT(1); O(ADD); LIT(45426); CALL(w_umstar); O(DROP); O(INV); LIT(1); O(ADD);
|
||||
j_ks = FWD(JUMP);
|
||||
HERE_(kp);
|
||||
LIT(45426); CALL(w_umstar); O(DROP);
|
||||
HERE_(j_ks);
|
||||
O(ADD); O(DUP); sp = FWD(MINUS_IF);
|
||||
O(DROP); LIT(-1); O(SEMI);
|
||||
HERE_(sp); O(DROP); LIT(0); O(SEMI);
|
||||
HERE_(zero);
|
||||
O(DROP);
|
||||
l_err = v4_asm_label(&as);
|
||||
O(DROP); O(DROP); LIT(NODE_ERROR); O(BANG_B); LIT(-1); O(STORE_B); LIT(0); LIT(0); O(SEMI);
|
||||
|
||||
v4_asm_resolve(&as, j_err, l_err);
|
||||
v4_asm_resolve(&as, j_test, l_test);
|
||||
}
|
||||
#undef D2SLASH
|
||||
#undef SUB_INLINE
|
||||
#undef SWAP_INLINE
|
||||
#undef VSET
|
||||
#undef VGET
|
||||
#undef JUMP_TO
|
||||
#undef HERE_
|
||||
#undef FWD
|
||||
|
||||
CHECK(v4_asm_ok(&as), "foundation words assemble");
|
||||
@@ -1034,7 +1188,7 @@ static int call(v4_cell word, unsigned argc, v4_cell a, v4_cell b, v4_cell c)
|
||||
if (argc > 0) v4_dstack_push(&n.ds, a);
|
||||
if (argc > 1) v4_dstack_push(&n.ds, b);
|
||||
if (argc > 2) v4_dstack_push(&n.ds, c);
|
||||
return v4_test_call(&n, &es, &h, word, 100000) > 0;
|
||||
return v4_test_call(&n, &es, &h, word, 4000000) > 0;
|
||||
}
|
||||
|
||||
/* The same with four arguments. */
|
||||
@@ -1048,7 +1202,7 @@ static int call4(v4_cell word, v4_cell a, v4_cell b, v4_cell c, v4_cell d)
|
||||
v4_dstack_push(&n.ds, b);
|
||||
v4_dstack_push(&n.ds, c);
|
||||
v4_dstack_push(&n.ds, d);
|
||||
return v4_test_call(&n, &es, &h, word, 100000) > 0;
|
||||
return v4_test_call(&n, &es, &h, word, 4000000) > 0;
|
||||
}
|
||||
|
||||
/* The results, top first, then the canary. */
|
||||
@@ -1422,6 +1576,42 @@ static uint64_t v3_q48_sqrt(uint64_t q)
|
||||
return x;
|
||||
}
|
||||
|
||||
/* v3's q48_log_approx, verbatim but for the names. */
|
||||
static uint64_t v3_q48_log(uint64_t x)
|
||||
{
|
||||
const uint64_t LN2_Q48 = 45426;
|
||||
int k = 0, iter;
|
||||
uint64_t m = x, y, result;
|
||||
|
||||
if (x == 0) return 0;
|
||||
if (x == 65536) return 0;
|
||||
if (m >= 131072) {
|
||||
while (m >= 131072) { m >>= 1; k++; }
|
||||
} else if (m < 65536) {
|
||||
while (m < 65536) { m <<= 1; k--; }
|
||||
}
|
||||
y = (m > 65536) ? (m - 65536) : 0;
|
||||
for (iter = 0; iter < 6; iter++) {
|
||||
uint64_t exp_y = v3_q48_exp(y), correction;
|
||||
int64_t delta_signed;
|
||||
if (exp_y == 0) break;
|
||||
delta_signed = (int64_t)m - (int64_t)exp_y;
|
||||
if (delta_signed > 0) {
|
||||
correction = v3_q48_div((uint64_t)delta_signed, exp_y);
|
||||
y = y + correction;
|
||||
} else if (delta_signed < 0) {
|
||||
correction = v3_q48_div((uint64_t)(-delta_signed), exp_y);
|
||||
if (y > correction) y = y - correction;
|
||||
else y = 0;
|
||||
}
|
||||
if (delta_signed < 100 && delta_signed > -100) break;
|
||||
}
|
||||
result = y;
|
||||
if (k > 0) result = result + v3_q48_mul((uint64_t)k << 16, LN2_Q48);
|
||||
else if (k < 0) result = result - v3_q48_mul((uint64_t)(-k) << 16, LN2_Q48);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Stack headroom. Run `word` with `dfill` marked cells under the canary and
|
||||
* `rfill` marked cells under its return address, and report whether the
|
||||
* results, the canary and every marked cell come back intact. The F18 stacks
|
||||
@@ -1440,7 +1630,7 @@ static int fits(v4_cell word, unsigned argc, const v4_cell *arg,
|
||||
v4_rstack_reset(&n.rs);
|
||||
v4_exec_reset(&es);
|
||||
for (i = 0; i < argc; i++) v4_dstack_push(&n.ds, arg[i]);
|
||||
if (v4_test_call(&n, &es, &h, word, 100000) <= 0) return 0;
|
||||
if (v4_test_call(&n, &es, &h, word, 4000000) <= 0) return 0;
|
||||
if (nres > V4_DATA_DEPTH) return 0;
|
||||
for (i = 0; i < nres; i++) want[i] = v4_dstack_pop(&n.ds);
|
||||
|
||||
@@ -1451,7 +1641,7 @@ static int fits(v4_cell word, unsigned argc, const v4_cell *arg,
|
||||
v4_dstack_push(&n.ds, CANARY);
|
||||
for (i = 0; i < argc; i++) v4_dstack_push(&n.ds, arg[i]);
|
||||
for (i = 0; i < rfill; i++) v4_rstack_push(&n.rs, RFILL(i));
|
||||
if (v4_test_call(&n, &es, &h, word, 100000) <= 0) return 0;
|
||||
if (v4_test_call(&n, &es, &h, word, 4000000) <= 0) return 0;
|
||||
for (i = 0; i < nres; i++) if (v4_dstack_pop(&n.ds) != want[i]) return 0;
|
||||
if (v4_dstack_pop(&n.ds) != CANARY) return 0;
|
||||
for (i = dfill; i-- > 0; ) if (v4_dstack_pop(&n.ds) != DFILL(i)) return 0;
|
||||
@@ -2105,6 +2295,43 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Q.LOG against v3's q48_log_approx for x > 0, bit for bit; x <= 0
|
||||
* gives 0 and NODE-ERROR (D-12). */
|
||||
{
|
||||
static const int64_t ql[] = {
|
||||
1, 2, 3, 0x7FFF, 0x8000, 0xFFFF, 0x10000, 0x10001, 0x18000, 0x1FFFF, 0x20000,
|
||||
0x2B7E1, 0x30000, 0xA0000, 0x640000, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF,
|
||||
0x100000000, ((int64_t)1 << 47), ((int64_t)1 << 62) + 12345,
|
||||
(int64_t)(((uint64_t)1 << 63) - 1u), 0, -1, -0x10000
|
||||
};
|
||||
uint64_t x = 0xA0761D6478BD642Fu;
|
||||
for (unsigned i = 0; i < sizeof ql / sizeof ql[0] + 2000u; i++) {
|
||||
int64_t q;
|
||||
v4_cell xl, xh, el, eh;
|
||||
if (i < sizeof ql / sizeof ql[0]) q = ql[i];
|
||||
else {
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
|
||||
q = (int64_t)((x >> 1) >> (x & 63u)); /* every magnitude */
|
||||
if (q == 0) q = 1;
|
||||
}
|
||||
qcells((uint64_t)q, &xl, &xh);
|
||||
if (q <= 0) { el = 0; eh = 0; }
|
||||
else qcells(v3_q48_log((uint64_t)q), &el, &eh);
|
||||
v4_node_store(&n, NODE_ERROR, 0);
|
||||
CHECK(call(w_qlog, 2, xl, xh, 0) && left2(el, eh)
|
||||
&& v4_node_load(&n, NODE_ERROR) == (q <= 0 ? -1 : 0),
|
||||
"Q.LOG [%u] x=%lld", i, (long long)q);
|
||||
}
|
||||
/* The reduced m takes only 65536 values, 1.0 <= m < 2.0; sweep every
|
||||
* 17th directly (k = 0), which covers the Newton iteration itself. */
|
||||
for (int64_t m = 65536; m < 131072; m += 17) {
|
||||
v4_cell xl, xh, el, eh;
|
||||
qcells((uint64_t)m, &xl, &xh);
|
||||
qcells(v3_q48_log((uint64_t)m), &el, &eh);
|
||||
CHECK(call(w_qlog, 2, xl, xh, 0) && left2(el, eh), "Q.LOG m=%lld", (long long)m);
|
||||
}
|
||||
}
|
||||
|
||||
/* Q./ at the overflow boundary: |a| * 2^16 against |b| * 2^(2N-1), for
|
||||
* divisors around 2^16 and 2^17, every sign. */
|
||||
{
|
||||
@@ -2218,6 +2445,12 @@ int main(void)
|
||||
};
|
||||
headroom("Q.SQRT", w_qsqrt, 2, qsqrt_args, 5, 2, &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "Q.SQRT leaves room");
|
||||
static const v4_cell qlog_args[][4] = {
|
||||
{ 0x10000, 0, 0, 0 }, { 0x2B7E1, 0, 0, 0 }, { 3, 0, 0, 0 }, { -1, 0x7FFF, 0, 0 },
|
||||
{ 0, 0, 0, 0 }, { 5, -1, 0, 0 }
|
||||
};
|
||||
headroom("Q.LOG", w_qlog, 2, qlog_args, 6, 2, &dh, &rh);
|
||||
CHECK(dh >= 2 && rh >= 2, "Q.LOG leaves room");
|
||||
headroom("SM/REM", w_smrem, 3, smrem_args, 5, 2, &dh, &rh);
|
||||
CHECK(rh >= 1, "SM/REM leaves room for /MOD's return address");
|
||||
headroom("/MOD", w_slashmod, 2, slashmod_args, 4, 2, &dh, &rh);
|
||||
|
||||
Reference in New Issue
Block a user