lib/math: re-fold F64_EXPBIAS to const f64info/f32info struct (#40)

Restore the Hare-faithful floatinfo struct defs (ref/hare/math/floats.ha
:117,126), removing the Drew-flattened-(b) #129-era bypass (F64_EXPBIAS:int
scalar). #149 (db7523e) now lowers cross-module &def to LEAQ, so the struct
exports are addressable via the stof consumer pattern (&math.f64info ->
f: *floatinfo -> f.expbias). First real consumer of A.2 struct-composite
static-init (DATA byte-validated: f64info/f32info 40B each).

ADD export def f64info/f32info: floatinfo (hex masks value-identical to
Hare's (1<<52)-1 etc.; A.2 helper folds bare literals only, documented
at-site). DELETE export def F64_EXPBIAS (zero consumers). KEEP NAN_BITS/
INF_BITS u64 sentinels (ruling b: def NAN=0.0/0.0 blocked by #147) +
F64_EXPONENT_BIAS:u64 (bit-ops alias, Hare keeps both).

Test 952: pointer-param row reads &math.f64info + &math.f32info through
*math.floatinfo, all 5 fields each (pointer-param not direct field-read,
dodges #150). Make test 184/184 incl 990-997 byte-id + combined_ww_fresh.
lib/math not compiler-imported -> no regen.

Unblocks fold-4 stof.ha.
This commit is contained in:
2026-05-27 10:55:31 +09:00
parent db7523e0e7
commit 88f3d67b28
2 changed files with 51 additions and 29 deletions

View File

@@ -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

View File

@@ -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 }