test(v4.0.0): execute Q.+ and Q.- against v3 Q48.16
DECOMPOSITION.md 5.26 makes Q.+ and Q.- the D+ and D- words. They are checked against v3's q48_add/q48_sub (uint64_t a + b, a - b, wrapping) on every pair of 15 edge Q values (0, ulp, 0.5, 1.0, 1.5, -1.0, -ulp, values across the 32-bit seam, Q max and min, +-12345.0) and 20000 pseudo-random pairs, 2682 of which overflow Q48.16. - 32-bit cells: bit-for-bit v3's result, overflow wrap included. - 64-bit cells, with a Q value as a sign-extended double: the low cell is v3's result; on overflow the high cell holds the true carry where v3 wraps. Whether a Q value is one cell or two on a 64-bit node is not ruled; recorded as open in 5.26. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6a371b850e
commit
5ac52f19d6
@@ -644,7 +644,7 @@ word. Per D-8, v4 Q values are signed.
|
||||
|
||||
| Word | Fate | Notes |
|
||||
| --- | --- | --- |
|
||||
| `Q.+` `Q.-` | CAP | `D+`, `D-` |
|
||||
| `Q.+` `Q.-` | CAP | `D+`, `D-` — executed on the golden model (2026-10-02) against v3's `q48_add`/`q48_sub` (`uint64_t` wrapping). At 32-bit cells the result is bit-for-bit v3's, overflow included. **Open:** at 64-bit cells a Q value as a sign-extended double has the same low cell as v3, but on Q48.16 overflow the high cell holds the true carry where v3 wraps. Whether a Q value is one cell or two on a 64-bit node is not yet ruled. |
|
||||
| `Q.*` | CAP | 64×64 product from four `UM*` partial products, shifted right 16. |
|
||||
| `Q./` | CAP | Shifted long division. **Division by zero sets an error** (v3 returned 0 silently). |
|
||||
| `Q.ABS` `Q.NEG` | CAP | `DABS`, `DNEGATE` |
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "v4/asm.h"
|
||||
#include "v4/testcode.h"
|
||||
#include "v4/umul.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures = 0, checks = 0;
|
||||
@@ -455,6 +456,46 @@ static int smrem_exact(v4_cell q, v4_cell nn, v4_cell r)
|
||||
return call(w_smrem, 3, (v4_cell)dl, (v4_cell)dh, nn) && left2(r, q);
|
||||
}
|
||||
|
||||
/* Q48.16 (section 5.26). v3's Q.+ and Q.- are uint64_t a + b and a - b,
|
||||
* wrapping (v3/include/q48_16.h). Here a Q value is a double: at 32-bit
|
||||
* cells its two halves, at 64-bit cells the value sign-extended (D-8). */
|
||||
static void qcells(uint64_t q, v4_cell *lo, v4_cell *hi)
|
||||
{
|
||||
#if V4_CELL_BITS == 32
|
||||
*lo = (v4_cell)(v4_ucell)(q & 0xFFFFFFFFu);
|
||||
*hi = (v4_cell)(v4_ucell)(q >> 32);
|
||||
#else
|
||||
*lo = (v4_cell)(v4_ucell)q;
|
||||
*hi = (q >> 63) ? V4_ALL_ONES : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Run Q.+ (the D+ word) or Q.- (the D- word) on a and b and compare with
|
||||
* v3's 64-bit wrapping result. At 32-bit cells the double must equal v3's
|
||||
* value bit for bit. At 64-bit cells the low cell must equal v3's value and
|
||||
* the high cell must be the true sign of the unwrapped result. */
|
||||
static int q_matches_v3(v4_cell word, uint64_t a, uint64_t b, uint64_t v3)
|
||||
{
|
||||
v4_cell al, ah, bl, bh, rl, rh;
|
||||
qcells(a, &al, &ah);
|
||||
qcells(b, &bl, &bh);
|
||||
if (!call4(word, al, ah, bl, bh)) return 0;
|
||||
#if V4_CELL_BITS == 32
|
||||
qcells(v3, &rl, &rh);
|
||||
return left2(rl, rh);
|
||||
#else
|
||||
{
|
||||
v4_ucell xl, xh, nl, nh;
|
||||
if (word == w_dminus) { dneg((v4_ucell)bl, (v4_ucell)bh, &nl, &nh); }
|
||||
else { nl = (v4_ucell)bl; nh = (v4_ucell)bh; }
|
||||
dadd((v4_ucell)al, (v4_ucell)ah, nl, nh, &xl, &xh);
|
||||
rl = (v4_cell)(v4_ucell)v3;
|
||||
rh = (v4_cell)xh;
|
||||
return (v4_ucell)rl == xl && left2(rl, rh);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -725,6 +766,37 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Q.+ and Q.- (section 5.26: the D+ and D- words) against v3. */
|
||||
{
|
||||
static const uint64_t qv[] = {
|
||||
0, 1, 0x8000u, 0x10000u, 0x18000u, /* 0, ulp, 0.5, 1.0, 1.5 */
|
||||
(uint64_t)0 - 0x10000u, (uint64_t)0 - 1u, /* -1.0, -ulp */
|
||||
0xFFFFu, 0x10001u, 0xFFFFFFFFu, 0x100000000u, /* across the 32-bit seam */
|
||||
((uint64_t)1 << 63) - 1u, (uint64_t)1 << 63, /* Q max, Q min */
|
||||
(uint64_t)12345 << 16, (uint64_t)0 - ((uint64_t)12345 << 16)
|
||||
};
|
||||
const unsigned nq = sizeof qv / sizeof qv[0];
|
||||
uint64_t x = 0x9E3779B97F4A7C15u;
|
||||
unsigned over = 0;
|
||||
|
||||
for (unsigned i = 0; i < nq; i++)
|
||||
for (unsigned j = 0; j < nq; j++) {
|
||||
CHECK(q_matches_v3(w_dplus, qv[i], qv[j], qv[i] + qv[j]), "Q.+ [%u,%u]", i, j);
|
||||
CHECK(q_matches_v3(w_dminus, qv[i], qv[j], qv[i] - qv[j]), "Q.- [%u,%u]", i, j);
|
||||
}
|
||||
for (unsigned i = 0; i < 20000; i++) {
|
||||
uint64_t a, b;
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17; a = x;
|
||||
x ^= x << 13; x ^= x >> 7; x ^= x << 17; b = x;
|
||||
if (i & 1u) a >>= (unsigned)(x & 63u); /* mixed magnitudes */
|
||||
if (i & 2u) b = (uint64_t)0 - b;
|
||||
if (((a + b) ^ a) & ((a + b) ^ b) & ((uint64_t)1 << 63)) over++;
|
||||
CHECK(q_matches_v3(w_dplus, a, b, a + b), "Q.+ random [%u]", i);
|
||||
CHECK(q_matches_v3(w_dminus, a, b, a - b), "Q.- random [%u]", i);
|
||||
}
|
||||
printf(" Q.+ random cases that overflow Q48.16: %u of 20000\n", over);
|
||||
}
|
||||
|
||||
/* Stack headroom of the two longest definitions. */
|
||||
{
|
||||
static const v4_cell umstar_args[][4] = {
|
||||
|
||||
Reference in New Issue
Block a user