lib/math: F32 family + f32bits/f32frombits + floatinfo struct (#106 fold-1a)

F32 width consts + bit converters mirror existing F64 family; floatinfo
struct definition (instances deferred to fold-1b per #129 cgen module-
let-init gap). Cite ref/hare/math/floats.ha; expbias: int per Drew.
This commit is contained in:
2026-05-26 19:10:02 +09:00
parent 0a2747ce5b
commit e9620953a0
2 changed files with 170 additions and 5 deletions

View File

@@ -1,11 +1,12 @@
// floats — f64 classification, sign, bit-reinterpret core, and the f64
// decompose half (subnormal-normalize + frexp). Ported from
// ref/hare/math/floats.ha (fold-1: classify/sign/bits; fold-2a:
// issubnormalf64/normalizef64/frexpf64). 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).
// f32 variants stay deferred (#104 blocks the f32 call-arg narrowing), as
// do the ldexp/modfrac/nextafter family (need f64 DIVIDE + the INF const).
// issubnormalf64/normalizef64/frexpf64; strconv-foundation fold:
// F32 bit-layout + f32bits/f32frombits + floatinfo struct type).
// 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 + the INF const).
package math;
@@ -17,12 +18,24 @@ export fn f64bits(n: f64) u64 = {
return *((&n): *u64);
};
// Returns the binary representation of the given f32.
// ref/hare/math/floats.ha:8
export fn f32bits(n: f32) u32 = {
return *((&n): *u32);
};
// Returns f64 with the given binary representation.
// ref/hare/math/floats.ha:11
export fn f64frombits(n: u64) f64 = {
return *((&n): *f64);
};
// Returns f32 with the given binary representation.
// ref/hare/math/floats.ha:14
export fn f32frombits(n: u32) f32 = {
return *((&n): *f32);
};
// ref/hare/math/floats.ha:17,20,23 declare these as untyped int. ww has
// no untyped def (every def carries a type) and routes shift/bitwise
// through unify_arith, which rejects mixed operand types (cmd/wcc/
@@ -64,6 +77,73 @@ 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;
// 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
// the note above F64_MANTISSA_BITS).
// The number of bits in the significand of the binary representation of f32.
// ref/hare/math/floats.ha:27
export def F32_MANTISSA_BITS: u32 = 23u32;
// The number of bits in the exponent of the binary representation of f32.
// ref/hare/math/floats.ha:30
export def F32_EXPONENT_BITS: u32 = 8u32;
// The bias of the exponent of the binary representation of f32. Subtract this
// from the exponent in the binary representation to get the actual exponent.
// ref/hare/math/floats.ha:33
export def F32_EXPONENT_BIAS: u32 = 127u32;
// Mask with each bit of an f32's mantissa set.
// ref/hare/math/floats.ha:43
export def F32_MANTISSA_MASK: u32 = (1u32 << F32_MANTISSA_BITS) - 1u32;
// Mask with each bit of an f32's exponent set.
// ref/hare/math/floats.ha:46
export def F32_EXPONENT_MASK: u32 = (1u32 << F32_EXPONENT_BITS) - 1u32;
// The mask that gets an f32's sign.
// ref/hare/math/floats.ha:87
def F32_SIGN_MASK: u32 = 1u32 << 31;
// Mask that clears an f32's exponent field, keeping sign + mantissa.
// ref/hare/math/floats.ha:92. Hare hardcodes the binary literal (its
// lexer can't const-fold the expression); ww's #88 def-const-fold can,
// so the readable form is kept (same call as F64_EXP_REMOVAL_MASK).
def F32_EXP_REMOVAL_MASK: u32 = ~(F32_EXPONENT_MASK << F32_MANTISSA_BITS);
// The f32 bit pattern whose exponent field evaluates to zero (0.5 scale).
// ref/hare/math/floats.ha:95
def F32_EXP_ZERO: u32 = (F32_EXPONENT_BIAS - 1u32) << F32_MANTISSA_BITS;
// floatinfo — IEEE-754 shape parameters for a binary float type, passed
// to width-generic helpers in strconv (eisel_lemire, floatbits, hex_to_bits,
// mkfloat). ref/hare/math/floats.ha:101. Hare's `int` maps to ww's `int`
// (machine word, 8B; project_int_machine_word_derived_limits), so the
// 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,
// Bits in exponent.
expbits: u64,
// Bias of exponent.
expbias: int,
// Mask for mantissa.
mantmask: u64,
// Mask for exponent.
expmask: u64,
};
// Returns true if the given floating-point number is NaN.
// ref/hare/math/floats.ha:144 (Hare's expression body inlined into a
// block: ww has no expression-bodied fn form, only brace blocks).