diff --git a/lib/math/floats.ww b/lib/math/floats.ww index 6582e1c1..38100f9c 100644 --- a/lib/math/floats.ww +++ b/lib/math/floats.ww @@ -3,8 +3,9 @@ // ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a: // issubnormalf64/normalizef64/frexpf64; strconv-foundation fold-1a: // F32 bit-layout + f32bits/f32frombits + floatinfo struct type; -// fold-1b: NAN_BITS/INF_BITS sentinels + F64_EXPBIAS (flattened from -// f64info per #129)). frexpf64's zero guard `n == 0f64` rides the #103 +// fold-1b: NAN_BITS/INF_BITS sentinels; γ-cleanup: f64info/f32info +// instances re-folded once #149 lowered &math.f64info). frexpf64's +// zero guard `n == 0f64` rides the #103 // fix (no-decimal f64 literal now materialized into XMM) and its // (f64, i64) tuple return rides the #105 fix (tuple f64-word read). // The ldexp/modfrac/nextafter family stays deferred (need f64 DIVIDE). @@ -78,15 +79,6 @@ def F64_EXP_REMOVAL_MASK: u64 = ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS); // ref/hare/math/floats.ha:84 def F64_EXP_ZERO: u64 = (F64_EXPONENT_BIAS - 1) << F64_MANTISSA_BITS; -// flattened from f64info per #129 (cgen module-level struct-init gap); -// cite ref/hare/math/floats.ha:117. Re-fold to const f64info: floatinfo -// when #129 closes (γ-cleanup pattern per amalloc-drop precedent). -// The existing F64_EXPONENT_BIAS:u64 above is the bit-ops alias; this -// int-typed F64_EXPBIAS is the floatinfo.expbias-field counterpart and -// drops the cast at the construction site (see ref/hare/strconv/stof.ha -// :248,288 for the consumer pattern). -export def F64_EXPBIAS: int = 1023; - // F32 bit-structure constants. ref/hare/math/floats.ha:27,30,33 declare // these as untyped int; ww has no untyped def, so they ride u32 (matching // the u32 bit container, the same way the F64 family rides u64 — see @@ -134,13 +126,6 @@ def F32_EXP_ZERO: u32 = (F32_EXPONENT_BIAS - 1u32) << F32_MANTISSA_BITS; // expbias field stays `int` — that keeps the fold-4 stof port byte-for-byte // against ref/hare/strconv/stof.ha:248,288 (`let e: int = 0` arithmetic // against `f.expbias` of the same type, no cast at use site). -// -// The companion `f64info` / `f32info` module-scope instances Hare exports -// (ref/hare/math/floats.ha:117,126) are DEFERRED — ww's cgen doesn't -// lower module-level struct lets-with-initializer (the symbol comes out -// undefined at link time). Callers in the fold-4 stof port construct a -// stack-local `floatinfo { ... }` and pass `&info` until that gap closes. -// Repro logged with team lead. export type floatinfo = struct { // Bits in significand. mantbits: u64, @@ -154,6 +139,31 @@ export type floatinfo = struct { expmask: u64, }; +// floatinfo instances for the f64 / f32 types, consumed by the +// width-generic strconv helpers via &math.f64info (cross-module +// address-of, lowered since #149). ref/hare/math/floats.ha:117,126. +// Hare spells the masks (1 << 52) - 1 / (1 << 23) - 1; the #129 A.2 +// struct-composite static-init path folds only bare-literal field +// initializers, not const-fold expressions, so the value-identical hex +// literals are used here (0xFFFFFFFFFFFFF == (1<<52)-1, 0x7FFFFF == +// (1<<23)-1 — same hex-literal style as the NAN_BITS/INF_BITS sentinels +// below). expbias rides `int` (the field type) with no suffix. +export def f64info: floatinfo = floatinfo { + mantbits = 52u64, + expbits = 11u64, + expbias = 1023, + mantmask = 0xFFFFFFFFFFFFFu64, + expmask = 0x7FFu64, +}; + +export def f32info: floatinfo = floatinfo { + mantbits = 23u64, + expbits = 8u64, + expbias = 127, + mantmask = 0x7FFFFFu64, + expmask = 0xFFu64, +}; + // IEEE-754 quiet-NaN and positive-Infinity f64 bit sentinels. // ref/hare/math/floats.ha:137,141. Hare exports `def NAN = 0.0/0.0;` and // `def INF = 1.0/0.0;` (untyped float def-fold); ww's cgen doesn't lower diff --git a/test/wcc/952_floats_run.c b/test/wcc/952_floats_run.c index cf7ba705..4a1656a6 100644 --- a/test/wcc/952_floats_run.c +++ b/test/wcc/952_floats_run.c @@ -327,20 +327,32 @@ static const struct row rows[] = { " if (math.isinf(1.23)) { return 7; };\n" " return 0;\n" "};\n", 0 }, - /* F64_EXPBIAS — the f64info.expbias-field-typed counterpart of the - * existing F64_EXPONENT_BIAS:u64. Flattened from f64info per #129. - * Round-trips into a floatinfo struct's expbias field with zero - * casts (the consumer pattern fold-4 stof will use). Cite ref/hare/ - * math/floats.ha:117 (f64info struct const) + :24 (the existing - * untyped F64_EXPONENT_BIAS). */ + /* f64info / f32info module-scope instances, read cross-module through + * a *floatinfo pointer parameter — the exact call shape stof's + * eisel_lemire (`f: *floatinfo`) uses in fold-4. Address-of a + * module-level def lowers to LEAQ since #149; the field reads + * round-trip the static-init DATA the #129 A.2 path emits. Mirrors + * ref/hare/math/floats.ha:117,126. Pointer-param (not direct + * math.f64info.expbias) because a cross-module struct-field direct read + * leaks the field name as an extern (#150, task list #48). */ { "package main;\n" "import math;\n" + "fn rdbias(f: *math.floatinfo) int = { return f.expbias; };\n" + "fn rdmant(f: *math.floatinfo) u64 = { return f.mantbits; };\n" + "fn rdmask(f: *math.floatinfo) u64 = { return f.mantmask; };\n" + "fn rdebits(f: *math.floatinfo) u64 = { return f.expbits; };\n" + "fn rdemask(f: *math.floatinfo) u64 = { return f.expmask; };\n" "export fn main() i32 = {\n" - " if (math.F64_EXPBIAS != 1023) { return 1; };\n" - " let info: math.floatinfo;\n" - " info.expbias = math.F64_EXPBIAS;\n" - " if (info.expbias != 1023) { return 2; };\n" - " if (info.expbias != math.F64_EXPBIAS) { return 3; };\n" + " if (rdbias(&math.f64info) != 1023) { return 1; };\n" + " if (rdmant(&math.f64info) != 52u64) { return 2; };\n" + " if (rdmask(&math.f64info) != 0xFFFFFFFFFFFFFu64) { return 3; };\n" + " if (rdbias(&math.f32info) != 127) { return 4; };\n" + " if (rdmant(&math.f32info) != 23u64) { return 5; };\n" + " if (rdmask(&math.f32info) != 0x7FFFFFu64) { return 6; };\n" + " if (rdebits(&math.f64info) != 11u64) { return 7; };\n" + " if (rdemask(&math.f64info) != 0x7FFu64) { return 8; };\n" + " if (rdebits(&math.f32info) != 8u64) { return 9; };\n" + " if (rdemask(&math.f32info) != 0xFFu64) { return 10; };\n" " return 0;\n" "};\n", 0 }, { NULL, 0 }