Files
scriptc/packages/runtime/test/gen-number-cases.mjs
2026-07-22 18:08:04 -05:00

89 lines
2.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Generates number-formatting test cases with Node as the oracle.
// Each line: <16 hex digits of the double's bit pattern>\t<String(value)>
//
// node gen-number-cases.mjs cases # 50k deterministic cases → stdout
// node gen-number-cases.mjs fuzz <count> # <count> random cases → stdout
import { argv, stdout } from "node:process";
const f64 = new Float64Array(1);
const u64 = new BigUint64Array(f64.buffer);
function line(x) {
f64[0] = x;
return `${u64[0].toString(16).padStart(16, "0")}\t${String(x)}\n`;
}
function* curated() {
yield* [
0, -0, 1, -1, 0.5, 0.1, 0.2, 0.3, 0.1 + 0.2, 2 / 3, 1 / 3,
NaN, Infinity, -Infinity,
Number.MAX_VALUE, Number.MIN_VALUE, -Number.MAX_VALUE, -Number.MIN_VALUE,
Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER + 1, Number.MAX_SAFE_INTEGER + 2,
Number.EPSILON, 2.2250738585072014e-308, // smallest normal
9007199254740993, 123456789012345678, 1.7976931348623155e308,
5e-324, 1e21, 1e-7, 1e-6, 999999999999999999999, 1000000000000000000000,
0.000001, 0.0000001, 123.456, 3.141592653589793, 2.718281828459045,
// %e/strtod tie-breaking nasties
5e-1, 5e1, 35, 8.5, 1.5e300, 6.34e-8, 4.35e-9,
];
// decimal-exponent boundary sweep: d × 10^e around the notation switches
for (let e = -10; e <= 25; e++) {
for (const d of [1, 2, 5, 9, 1.5, 9.99]) yield d * 10 ** e;
}
// powers of 2 and their neighbors across the full range
for (let e = -1074; e <= 1023; e += 7) {
const x = 2 ** e;
yield* [x, -x, x * (1 + Number.EPSILON), x * (1 - Number.EPSILON / 2)];
}
// integers near digit-length transitions
for (let d = 1; d <= 21; d++) {
const x = 10 ** d;
yield* [x - 1, x, x + 1];
}
}
// xorshift128 — deterministic so the committed file is reproducible
function makeRng(seed) {
let s0 = seed ^ 0x9e3779b97f4a7c15n, s1 = 0x2545f4914f6cdd1dn;
return () => {
let x = s0;
const y = s1;
s0 = y;
x ^= (x << 23n) & 0xffffffffffffffffn;
s1 = x ^ y ^ (x >> 17n) ^ (y >> 26n);
return (s1 + y) & 0xffffffffffffffffn;
};
}
function* random(count, seed) {
const next = makeRng(seed);
let emitted = 0;
while (emitted < count) {
u64[0] = next();
yield f64[0];
emitted++;
}
}
const mode = argv[2] ?? "cases";
let chunk = "";
function emit(x) {
chunk += line(x);
if (chunk.length > 1 << 16) {
stdout.write(chunk);
chunk = "";
}
}
if (mode === "cases") {
for (const x of curated()) emit(x);
for (const x of random(50_000, 0x7357cafen)) emit(x);
} else if (mode === "fuzz") {
const count = Number(argv[3] ?? 1_000_000);
const seed = BigInt(Date.now());
for (const x of random(count, seed)) emit(x);
} else {
throw new Error(`unknown mode: ${mode}`);
}
stdout.write(chunk);