// Ported from 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; γ-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). package math; // ref/hare/math/floats.ha:5. Parens around &n are load-bearing: ww's `:` // cast binds tighter than unary `&`, so Hare's `*(&n: *u64)` would parse // as `*(&(n: *u64))`; `(&n): *u64` reinterprets the address as intended. export fn f64bits(n: f64) u64 = { return *((&n): *u64); }; // ref/hare/math/floats.ha:8 export fn f32bits(n: f32) u32 = { return *((&n): *u32); }; // ref/hare/math/floats.ha:11 export fn f64frombits(n: u64) f64 = { return *((&n): *f64); }; // 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/ // check.c:769). The bit-structure consts are used only as u64 shift // amounts and mask widths, so they are typed u64 here — the closest // stand-in for Hare's untyped-int adapt at those use sites. export def F64_MANTISSA_BITS: u64 = 52; export def F64_EXPONENT_BITS: u64 = 11; // The bias of the exponent of the binary representation of f64. Subtract this // from the exponent in the binary representation to get the actual exponent. export def F64_EXPONENT_BIAS: u64 = 1023; // ref/hare/math/floats.ha:37 export def F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_BITS) - 1; // ref/hare/math/floats.ha:40 export def F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_BITS) - 1; // ref/hare/math/floats.ha:75 def F64_SIGN_MASK: u64 = 1u64 << 63; // Mask that clears an f64's exponent field, keeping sign + mantissa. // ref/hare/math/floats.ha:77. Hare hardcodes the 0x800FFFFFFFFFFFFF binary // literal because its lexer can't const-fold the expression; ww's #88 // def-const-fold can, so the readable form is kept. floats.ha:79's NOTE // expression has an `0u64 &` upstream typo (it would yield 0); the value it // documents is exactly ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS). def F64_EXP_REMOVAL_MASK: u64 = ~(F64_EXPONENT_MASK << F64_MANTISSA_BITS); // The f64 bit pattern whose exponent field evaluates to zero (0.5 scale). // 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). // ref/hare/math/floats.ha:27 export def F32_MANTISSA_BITS: u32 = 23u32; // 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; // ref/hare/math/floats.ha:43 export def F32_MANTISSA_MASK: u32 = (1u32 << F32_MANTISSA_BITS) - 1u32; // ref/hare/math/floats.ha:46 export def F32_EXPONENT_MASK: u32 = (1u32 << F32_EXPONENT_BITS) - 1u32; // 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). export type floatinfo = struct { mantbits: u64, expbits: u64, expbias: int, mantmask: u64, 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 // `def: f64 = expr;` (the symbol comes out undefined at link time — see // #129). Callers materialize the f64 sentinel via f64frombits(NAN_BITS) // / f64frombits(INF_BITS); same bit-exact value, one extra reinterpret. // 0x7FF8000000000000 is the IEEE-754 binary64 quiet-NaN (sign=0, exp= // all-ones, mantissa MSB=1, rest=0); 0x7FF0000000000000 is +Infinity // (sign=0, exp=all-ones, mantissa=0). Re-fold to `def NAN: f64 = ...` // when #129 closes (γ-cleanup pattern per amalloc-drop precedent). export def NAN_BITS: u64 = 0x7FF8000000000000u64; export def INF_BITS: u64 = 0x7FF0000000000000u64; // ref/hare/math/floats.ha:144 (Hare's expression body inlined into a // block: ww has no expression-bodied fn form, only brace blocks). export fn isnan(n: f64) bool = { return n != n; }; // ref/hare/math/floats.ha:147 export fn isinf(n: f64) bool = { const bits = f64bits(n); const mant = bits & F64_MANTISSA_MASK; const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; return exp == F64_EXPONENT_MASK && mant == 0; }; // ref/hare/math/floats.ha:179 export fn issubnormalf64(n: f64) bool = { const bits = f64bits(n); const mant = bits & F64_MANTISSA_MASK; const exp = bits >> F64_MANTISSA_BITS & F64_EXPONENT_MASK; return exp == 0 && mant != 0; }; // ref/hare/math/floats.ha:195 export fn absf64(n: f64) f64 = { if (isnan(n)) { return n; }; return f64frombits(f64bits(n) & ~F64_SIGN_MASK); }; // Returns 1 if x is positive and -1 if x is negative. Note that zero is also // signed. // ref/hare/math/floats.ha:212 export fn signf64(x: f64) i64 = { if (f64bits(x) & F64_SIGN_MASK == 0) { return 1i64; } else { return -1i64; }; }; // ref/hare/math/floats.ha:231 export fn ispositivef64(x: f64) bool = { return signf64(x) == 1i64; }; // ref/hare/math/floats.ha:237 export fn isnegativef64(x: f64) bool = { return signf64(x) == -1i64; }; // ref/hare/math/floats.ha:243 export fn copysignf64(x: f64, y: f64) f64 = { return f64frombits((f64bits(x) & ~F64_SIGN_MASK) | (f64bits(y) & F64_SIGN_MASK)); }; // Takes a potentially subnormal f64 n and returns a normal f64 normal_float // and an exponent exp such that n == normal_float * 2^{exp}. // ref/hare/math/floats.ha:256 export fn normalizef64(n: f64) (f64, i64) = { if (issubnormalf64(n)) { const factor = 1i64 << (F64_MANTISSA_BITS: i64); const normal_float = (n * (factor: f64)); return (normal_float, -(F64_MANTISSA_BITS: i64)); }; return (n, 0); }; // Breaks a f64 down into its mantissa and exponent. The mantissa will be // between 0.5 and 1. // ref/hare/math/floats.ha:278 export fn frexpf64(n: f64) (f64, i64) = { if (isnan(n) || isinf(n) || n == 0f64) { return (n, 0); }; const normalized = normalizef64(n); const normal_float = normalized.0; const normalization_exp = normalized.1; const bits = f64bits(normal_float); const raw_exp: u64 = (bits >> F64_MANTISSA_BITS) & F64_EXPONENT_MASK; const exp: i64 = normalization_exp + (raw_exp: i64) - (F64_EXPONENT_BIAS: i64) + 1; const mantissa: f64 = f64frombits((bits & F64_EXP_REMOVAL_MASK) | F64_EXP_ZERO); return (mantissa, exp); };