lib/strconv: f32tos Ryū shortest float→string (#106 fold-5b)
The f32 coda of Drew's strconv 5-fold plan — f64tos shipped in fold-5a (0e66073); this completes the plan. Re-lands the f32-exclusive Ryū path that fold-5a removed under the dead-code rule (it was #143-blocked): pow5fac32/pow5multiple32/pow2multiple32, mulshift32, mulpow5inv_divpow2/ mulpow5_divpow2, decf32, f32todecf32 (ftos_ryu.ha), and the f32tos driver (ftos.ha:448). Plus F32_POW5_*_BITCOUNT in ftos_data.ww. The f32 path REUSES the shared u64 core (mulshiftall64/u128mul/u128rshift/ log*) and f64computeinvpow5/f64computepow5 — and thus the f64 SPLIT2 tables — exactly as ftos_ryu.ha does; there are no separate f32 tables. Unblocked by #143 (aff7725): f32tos calls math.f32bits(n), passing an f32 arg, which now spills MOVSS (4B) in both stages. Verified: f32tos's arg push/pop is MOVSS, w6c vs w6c_ww 0-diff on the strconv-embedding combined.ww (w6c/wwdump/smoke regenerated). Dodges (cgen bugs still deferred, each cited at-site): decf32.exponent:i64 sidesteps the #169 narrow-second-field struct-return unpack (byte-id gate-confirmed, not an ABI guarantee); #168 div/mod local-bind on the two `%10` sites; *decimal pointer field reads (#170); [32]u8 buffer reuses f64tos's byte-id-clean band over Hare's [14] (#43). mulshift32's U32_MAX bound inlines the literal — ww's types.U32_MAX is package-private (#172). Test: ftostest.ww gains f32 vectors — the tcs G/void rows (shared f32/f64 shortest), the f32-exclusive tcsf32 extremes (1e-45 / 1.1754944e-38 / 3.4028235e38, full 24-bit mantissa), specials, a negative-normal, and 33554432 (the sole e2>=0/q<=9 runtime cover). make test 187/187 incl 908_ftos_run + 990-997 byte-id + combined_ww_fresh.
This commit is contained in:
@@ -4,15 +4,14 @@
|
|||||||
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
||||||
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
||||||
//
|
//
|
||||||
// SCOPE — the f64tos shortest-representation subset (Hare's ffmt::G,
|
// SCOPE — the f64tos + f32tos shortest-representation subset (Hare's
|
||||||
// prec=void, fflags::NONE). Two deferrals:
|
// ffmt::G, prec=void, fflags::NONE). f32tos (ftos.ha:448) + its f32 Ryū
|
||||||
// - f32tos (ftos.ha:448) + its f32 Ryū sub-path (f32todecf32 +
|
// sub-path (f32todecf32 + mulpow5inv/pow5_divpow2 + mulshift32 + the *32
|
||||||
// mulpow5inv/pow5_divpow2 + mulshift32 + the *32 helpers + the
|
// helpers, reusing the shared u64-core + the f64 SPLIT2 tables — the f32
|
||||||
// F32_POW5_*_BITCOUNT defs) → fold-5b (task #67), gated on the #143
|
// path has no separate tables, matching ftos_ryu.ha) ship here in fold-5b
|
||||||
// f32-arg-push cgen fix: f32tos must call math.f32bits(n), passing
|
// (task #67): the gating #143 f32-arg-push cgen fix landed (aff7725, MOVSS
|
||||||
// an f32 arg, which spills MOVSD (cstage) vs MOVSS (wwstage) →
|
// both stages), so f32tos's math.f32bits(n) call — passing an f32 arg — is
|
||||||
// 990-997 byte-id break. Not isolatable (f32bits IS the reinterpret),
|
// now byte-id-clean. One deferral remains:
|
||||||
// only fixable. f32tos IS a real Hare entry — it ships in #67.
|
|
||||||
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
||||||
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
||||||
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
||||||
@@ -340,6 +339,195 @@ fn f64todecf64(mantissa: u64, exponent: u32) decf64 = {
|
|||||||
return decf64 { exponent = (exp: i64), mantissa = output };
|
return decf64 { exponent = (exp: i64), mantissa = output };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==== f32 Ryū sub-path (ftos_ryu.ha). The *32 helpers below mirror their
|
||||||
|
// u64 siblings at 32-bit width; they reuse the SHARED f64computeinvpow5/
|
||||||
|
// f64computepow5 (and thus the f64 SPLIT2 tables) per ftos_ryu.ha — there
|
||||||
|
// is no separate f32 table. Same scalar-PARAM-mutation → copy-to-local,
|
||||||
|
// comma-split, assert → os.assert, expr-yield → block divergences as the
|
||||||
|
// f64 path above. ====
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:52. Largest p with 5^p | value (32-bit).
|
||||||
|
fn pow5fac32(v: u32) u32 = {
|
||||||
|
let value: u32 = v;
|
||||||
|
let count: u32 = 0u32;
|
||||||
|
for (true) {
|
||||||
|
os.assert(value != 0u32, "strconv.pow5fac32: value == 0");
|
||||||
|
let q: u32 = value / 5u32;
|
||||||
|
let r: u32 = value % 5u32;
|
||||||
|
if (r != 0u32) { break; };
|
||||||
|
value = q;
|
||||||
|
count += 1u32;
|
||||||
|
};
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:67.
|
||||||
|
fn pow5multiple32(v: u32, p: u32) bool = { return pow5fac32(v) >= p; };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:75.
|
||||||
|
fn pow2multiple32(v: u32, p: u32) bool = {
|
||||||
|
os.assert(v > 0u32, "strconv.pow2multiple32: v == 0");
|
||||||
|
os.assert(p < 32u32, "strconv.pow2multiple32: p >= 32");
|
||||||
|
return (v & ((1u32 << p) - 1u32)) == 0u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:121. `m * a_lo` etc. carry an explicit
|
||||||
|
// (m: u64) cast (Hare promotes the u32 operand; ww is strict). The bound
|
||||||
|
// assert inlines U32_MAX's value: ww's types.U32_MAX is package-private
|
||||||
|
// (lib/types/types.ww — no `export`), so Hare's `types::U32_MAX` can't be
|
||||||
|
// referenced cross-package.
|
||||||
|
fn mulshift32(m: u32, a: u64, s: u32) u32 = {
|
||||||
|
os.assert(s > 32u32, "strconv.mulshift32: s <= 32");
|
||||||
|
let a_lo: u64 = (a: u32): u64;
|
||||||
|
let a_hi: u64 = a >> 32u64;
|
||||||
|
let b0: u64 = (m: u64) * a_lo;
|
||||||
|
let b1: u64 = (m: u64) * a_hi;
|
||||||
|
let sum: u64 = (b0 >> 32u64) + b1;
|
||||||
|
let ss: u64 = sum >> ((s: u64) - 32u64);
|
||||||
|
os.assert(ss <= 4294967295u64, "strconv.mulshift32: ss > U32_MAX");
|
||||||
|
return ss: u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:130.
|
||||||
|
fn mulpow5inv_divpow2(m: u32, q: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computeinvpow5(q);
|
||||||
|
return mulshift32(m, pow5.1 + 1u64, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:135.
|
||||||
|
fn mulpow5_divpow2(m: u32, i: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computepow5(i);
|
||||||
|
return mulshift32(m, pow5.1, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:387. `exponent` rides i64 not Hare's i32,
|
||||||
|
// for the same reason decf64 does: widening the field to a full second
|
||||||
|
// eightbyte SIDESTEPS the #169 narrow-i32-field struct-return unpack (a
|
||||||
|
// narrow i32 there unpacks MOVL wwstage vs MOVQ cstage). The value always
|
||||||
|
// fits i32 (cast at the init_dec_mant_exp call site). `mantissa` stays u32
|
||||||
|
// (Hare's width); the {u32, pad, i64} layout's first eightbyte holds
|
||||||
|
// mantissa@0 + 4B pad and reads cleanly — byte-id CONFIRMED by the 990-997
|
||||||
|
// gate (0-diff cs vs ww), not relied on as an ABI guarantee.
|
||||||
|
type decf32 = struct { mantissa: u32, exponent: i64 };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:392. Shortest decimal of an f32:
|
||||||
|
// value == mantissa * 10^exponent. `mantissa`/`exponent` are the raw
|
||||||
|
// IEEE-754 fields of an f32.
|
||||||
|
fn f32todecf32(mantissa: u32, exponent: u32) decf32 = {
|
||||||
|
let e2: i32 = (math.F32_EXPONENT_BIAS + math.F32_MANTISSA_BITS + 2u32): i32;
|
||||||
|
let m2: u32 = 0u32;
|
||||||
|
if (exponent == 0u32) {
|
||||||
|
e2 = 1i32 - e2;
|
||||||
|
m2 = mantissa;
|
||||||
|
} else {
|
||||||
|
e2 = (exponent: i32) - e2;
|
||||||
|
m2 = (1u32 << math.F32_MANTISSA_BITS) | mantissa;
|
||||||
|
};
|
||||||
|
let accept_bounds: bool = (m2 & 1u32) == 0u32;
|
||||||
|
let mv: u32 = 4u32 * m2;
|
||||||
|
let mp: u32 = mv + 2u32;
|
||||||
|
let mm_shift: u32 = ibool(mantissa != 0u32 || exponent <= 1u32): u32;
|
||||||
|
let mm: u32 = mv - 1u32 - mm_shift;
|
||||||
|
let vr: u32 = 0u32;
|
||||||
|
let vp: u32 = 0u32;
|
||||||
|
let vm: u32 = 0u32;
|
||||||
|
let e10: i32 = 0i32;
|
||||||
|
let vm_trailing_zeroes: bool = false;
|
||||||
|
let vr_trailing_zeroes: bool = false;
|
||||||
|
let last_removed_digit: u8 = 0u8;
|
||||||
|
if (e2 >= 0i32) {
|
||||||
|
let q: u32 = log10pow2(e2: u32);
|
||||||
|
e10 = q: i32;
|
||||||
|
let k: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q) - 1u32;
|
||||||
|
let i: i32 = -e2 + ((q + k): i32);
|
||||||
|
vr = mulpow5inv_divpow2(mv, q, i);
|
||||||
|
vp = mulpow5inv_divpow2(mp, q, i);
|
||||||
|
vm = mulpow5inv_divpow2(mm, q, i);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
let l: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q - 1u32) - 1u32;
|
||||||
|
// #168 dodge: div/mod on an inline N_CALL result emits a
|
||||||
|
// signed IDIVQ in wwstage; bind the call to a local first.
|
||||||
|
let lrd: u32 = mulpow5inv_divpow2(mv, q - 1u32,
|
||||||
|
-e2 + ((q + l): i32) - 1i32);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 9u32) {
|
||||||
|
if (mv % 5u32 == 0u32) {
|
||||||
|
vr_trailing_zeroes = pow5multiple32(mv, q);
|
||||||
|
} else if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = pow5multiple32(mm, q);
|
||||||
|
} else {
|
||||||
|
vp -= (ibool(pow5multiple32(mp, q)): u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let q: u32 = log10pow5((-e2): u32);
|
||||||
|
e10 = (q: i32) + e2;
|
||||||
|
let i: u32 = (-e2 - (q: i32)): u32;
|
||||||
|
let k: u32 = pow5bits(i) - (F32_POW5_BITCOUNT: u32);
|
||||||
|
let j: i32 = (q: i32) - (k: i32);
|
||||||
|
vr = mulpow5_divpow2(mv, i, j);
|
||||||
|
vp = mulpow5_divpow2(mp, i, j);
|
||||||
|
vm = mulpow5_divpow2(mm, i, j);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
j = (q: i32) - 1i32 - ((pow5bits(i + 1u32): i32) - (F32_POW5_BITCOUNT: i32));
|
||||||
|
// #168 dodge (see above): local-bind before the % 10.
|
||||||
|
let lrd: u32 = mulpow5_divpow2(mv, (i + 1u32), j);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 1u32) {
|
||||||
|
vr_trailing_zeroes = true;
|
||||||
|
if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = mm_shift == 1u32;
|
||||||
|
} else {
|
||||||
|
vp -= 1u32;
|
||||||
|
};
|
||||||
|
} else if (q < 31u32) {
|
||||||
|
vr_trailing_zeroes = pow2multiple32(mv, q - 1u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let removed: i32 = 0i32;
|
||||||
|
let output: u32 = 0u32;
|
||||||
|
if (vm_trailing_zeroes || vr_trailing_zeroes) {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
vm_trailing_zeroes = vm_trailing_zeroes && ((vm - (vm / 10u32) * 10u32) == 0u32);
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
if (vm_trailing_zeroes) {
|
||||||
|
for ((vm % 10u32) == 0u32) {
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (vr_trailing_zeroes && last_removed_digit == 5u8 && vr % 2u32 == 0u32) {
|
||||||
|
last_removed_digit = 4u8; // round to even
|
||||||
|
};
|
||||||
|
let cond1: bool = (vr == vm) && ((!accept_bounds) || (!vm_trailing_zeroes));
|
||||||
|
let cond2: bool = last_removed_digit >= 5u8;
|
||||||
|
output = vr + (ibool(cond1 || cond2): u32);
|
||||||
|
} else {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
output = vr + (ibool(vr == vm || last_removed_digit >= 5u8): u32);
|
||||||
|
};
|
||||||
|
let exp: i32 = e10 + removed;
|
||||||
|
return decf32 { mantissa = output, exponent = (exp: i64) };
|
||||||
|
};
|
||||||
|
|
||||||
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
||||||
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
||||||
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
||||||
@@ -547,3 +735,64 @@ export fn f64tos(n: f64) str = {
|
|||||||
r.len = o;
|
r.len = o;
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos.ha:448. f32 → shortest base-10 str. Same static-
|
||||||
|
// buffer convention + G/void/NONE-inlined path as f64tos. f32bits(n)
|
||||||
|
// passes an f32 arg → MOVSS both stages post-#143 (aff7725); this is the
|
||||||
|
// piece fold-5b was gated on.
|
||||||
|
//
|
||||||
|
// Hare sizes this [14]u8 (ftos.ha:451: 1 + 1 + 1 + 7 + 1 + 1 + 2). Sized
|
||||||
|
// 32 to reuse f64tos's proven byte-id-clean band: a no-rhs [N]u8 module
|
||||||
|
// buffer at the size-16/24 band emits divergent DATAW counts cs≠ww (#43);
|
||||||
|
// 32 emits 2 DATAW in both. The unused tail bytes are harmless.
|
||||||
|
let f32tos_buf: [32]u8;
|
||||||
|
|
||||||
|
export fn f32tos(n: f32) str = {
|
||||||
|
let bits: u32 = math.f32bits(n);
|
||||||
|
let mantissa: u32 = bits & math.F32_MANTISSA_MASK;
|
||||||
|
let exponent: u32 = (bits >> math.F32_MANTISSA_BITS) & math.F32_EXPONENT_MASK;
|
||||||
|
let sign: bool = (bits >> (math.F32_EXPONENT_BITS + math.F32_MANTISSA_BITS)) > 0u32;
|
||||||
|
let special: bool = exponent == math.F32_EXPONENT_MASK;
|
||||||
|
|
||||||
|
let o: i32 = 0i32;
|
||||||
|
let r: str;
|
||||||
|
r.ptr = &f32tos_buf[0];
|
||||||
|
// NaN carries no sign prefix (ftos.ha:331-333, before sign handling).
|
||||||
|
if (special && mantissa != 0u32) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "nan");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (sign) {
|
||||||
|
f32tos_buf[o] = 45u8; // '-'
|
||||||
|
o += 1i32;
|
||||||
|
};
|
||||||
|
if (special) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "infinity");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (exponent == 0u32 && mantissa == 0u32) {
|
||||||
|
f32tos_buf[o] = 48u8; // '0' (encode_zero, G/void/NONE)
|
||||||
|
o += 1i32;
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = decimal { ... };
|
||||||
|
// *decimal pointer for the field reads (the #170 dodge; see f64tos).
|
||||||
|
let pd: *decimal = &d;
|
||||||
|
let dd: decf32 = f32todecf32(mantissa, exponent);
|
||||||
|
init_dec_mant_exp(pd, (dd.mantissa: u64), (dd.exponent: i32));
|
||||||
|
trim(pd);
|
||||||
|
if (pd.nd == (0u64: size)) {
|
||||||
|
f32tos_buf[o] = 48u8; // rounded to zero
|
||||||
|
o += 1i32;
|
||||||
|
} else if (pd.dp < -1i32 || (pd.dp - (pd.nd: i32)) > 2i32) {
|
||||||
|
o = encode_e_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
} else {
|
||||||
|
o = encode_f_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
};
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|||||||
@@ -25,12 +25,18 @@ package strconv;
|
|||||||
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
||||||
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
||||||
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
||||||
// The F32_POW5_*_BITCOUNT siblings (ftos_ryu.ha:162-163) land in fold-5b
|
|
||||||
// (task #67) with their only consumer, f32todecf32 — omitted here to keep
|
|
||||||
// this fold dead-code-free.
|
|
||||||
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
||||||
def F64_POW5_BITCOUNT: u8 = 125u8;
|
def F64_POW5_BITCOUNT: u8 = 125u8;
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:162-163. The f32 split-table bit-counts,
|
||||||
|
// derived from the f64 siblings (Hare: F64_..._BITCOUNT - 64). Consumed by
|
||||||
|
// f32todecf32 (ftos.ww), landed in fold-5b (task #67) — the f32 path reuses
|
||||||
|
// the f64 SPLIT2 tables (via f64computeinvpow5/f64computepow5), so no
|
||||||
|
// separate F32 tables exist (matches ftos_ryu.ha). u8 like the f64 defs;
|
||||||
|
// ftos.ww casts to u32/i32 at each use.
|
||||||
|
def F32_POW5_INV_BITCOUNT: u8 = F64_POW5_INV_BITCOUNT - 64u8;
|
||||||
|
def F32_POW5_BITCOUNT: u8 = F64_POW5_BITCOUNT - 64u8;
|
||||||
|
|
||||||
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
||||||
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
||||||
[1u64, 2305843009213693952u64],
|
[1u64, 2305843009213693952u64],
|
||||||
|
|||||||
@@ -10,8 +10,11 @@
|
|||||||
// math.f64frombits of the IEEE bit patterns (ww math has no f64 NAN/INF
|
// math.f64frombits of the IEEE bit patterns (ww math has no f64 NAN/INF
|
||||||
// const — see math/floats.ww).
|
// const — see math/floats.ww).
|
||||||
//
|
//
|
||||||
// f32tos is NOT exercised here: it is deferred to fold-5b (task #67),
|
// f32tos (fold-5b, task #67) is exercised by f32tos_fixed/_scientific/
|
||||||
// gated on the #143 f32-arg-push cs≠ww cgen fix.
|
// _extremes/_special below. The G/void rows mirror ftos_test.ha's tcs (the
|
||||||
|
// "pass for both f32 and f64" set, lines 9/183); the _extremes rows mirror
|
||||||
|
// tcsf32 (ftos_test.ha:218-221, the f32-EXCLUSIVE denormal/min/max — full
|
||||||
|
// f32 mantissa). f32 values use the `f32` literal suffix or f32frombits.
|
||||||
//
|
//
|
||||||
// Lives in lib/strconv/test/ so `import strconv` resolves to the
|
// Lives in lib/strconv/test/ so `import strconv` resolves to the
|
||||||
// DIRECTORY (full package), not the strconv.ww FILE — same rationale as
|
// DIRECTORY (full package), not the strconv.ww FILE — same rationale as
|
||||||
@@ -84,10 +87,78 @@ fn chk(n: f64, want: str) bool = {
|
|||||||
if (!chk(nan, "nan")) { fail(); };
|
if (!chk(nan, "nan")) { fail(); };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn chkf32(n: f32, want: str) bool = {
|
||||||
|
return streq(f32tos(n), want);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ftos_test.ha tcs G/void rows (the "pass for both f32 and f64" set) —
|
||||||
|
// fixed-point renders. 1.0/1.5/0.1/100/0.5 are the fold-5a probe's 5/5
|
||||||
|
// value-validated f32todecf32 pairs.
|
||||||
|
@test fn f32tos_fixed() void = {
|
||||||
|
if (!chkf32(13.37f32, "13.37")) { fail(); };
|
||||||
|
if (!chkf32(-13.37f32, "-13.37")) { fail(); }; // sign prefix + encode_f at o=1
|
||||||
|
// 2^25: the smallest f32 with e2 >= 0 (q == 0) — the ONLY runtime cover
|
||||||
|
// of f32todecf32's e2>=0 / q<=9 trailing-zero block (the f32-specific
|
||||||
|
// `q <= 9` + pow5multiple32 arm). External shortest-round-trip check.
|
||||||
|
if (!chkf32(33554432.0f32, "33554432")) { fail(); };
|
||||||
|
if (!chkf32(12345.0f32, "12345")) { fail(); };
|
||||||
|
if (!chkf32(1100.0f32, "1100")) { fail(); };
|
||||||
|
if (!chkf32(100.0f32, "100")) { fail(); };
|
||||||
|
if (!chkf32(10.0f32, "10")) { fail(); };
|
||||||
|
if (!chkf32(1.0f32, "1")) { fail(); };
|
||||||
|
if (!chkf32(1.5f32, "1.5")) { fail(); };
|
||||||
|
if (!chkf32(0.5f32, "0.5")) { fail(); };
|
||||||
|
if (!chkf32(0.1f32, "0.1")) { fail(); };
|
||||||
|
if (!chkf32(0.01f32, "0.01")) { fail(); };
|
||||||
|
if (!chkf32(0.011f32, "0.011")) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ftos_test.ha tcs G/void rows — scientific (shortest-G E-dispatch).
|
||||||
|
@test fn f32tos_scientific() void = {
|
||||||
|
if (!chkf32(10000.0f32, "1e4")) { fail(); };
|
||||||
|
if (!chkf32(11000.0f32, "1.1e4")) { fail(); };
|
||||||
|
if (!chkf32(1000.0f32, "1e3")) { fail(); };
|
||||||
|
if (!chkf32(0.001f32, "1e-3")) { fail(); };
|
||||||
|
if (!chkf32(0.0011f32, "1.1e-3")) { fail(); };
|
||||||
|
if (!chkf32(0.0001f32, "1e-4")) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ftos_test.ha:218-221 (tcsf32) — the f32-EXCLUSIVE denormal/min-normal/
|
||||||
|
// max-normal rows (distinct outputs from f64): exercise the full f32
|
||||||
|
// mantissa (8 sig digits) + 2-digit exponent. Built via f32frombits of the
|
||||||
|
// IEEE-754 bit patterns (ww math has no F32_MIN_*/MAX_NORMAL const).
|
||||||
|
@test fn f32tos_extremes() void = {
|
||||||
|
let minsub: f32 = math.f32frombits(1u32); // 2^-149
|
||||||
|
if (!chkf32(minsub, "1e-45")) { fail(); };
|
||||||
|
let minnorm: f32 = math.f32frombits(0x00800000u32); // 2^-126
|
||||||
|
if (!chkf32(minnorm, "1.1754944e-38")) { fail(); };
|
||||||
|
let maxnorm: f32 = math.f32frombits(0x7F7FFFFFu32);
|
||||||
|
if (!chkf32(maxnorm, "3.4028235e38")) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ftos_test.ha:11/15/26 — zero, ±infinity, nan (f32 bit patterns).
|
||||||
|
@test fn f32tos_special() void = {
|
||||||
|
if (!chkf32(0.0f32, "0")) { fail(); };
|
||||||
|
// sign bit via (1u32 << 31), mirroring the f64 test's I64_MIN-literal
|
||||||
|
// avoidance (#144 family).
|
||||||
|
let negzero: f32 = math.f32frombits(1u32 << 31u32);
|
||||||
|
if (!chkf32(negzero, "-0")) { fail(); };
|
||||||
|
let inf: f32 = math.f32frombits(0x7F800000u32);
|
||||||
|
if (!chkf32(inf, "infinity")) { fail(); };
|
||||||
|
let ninf: f32 = math.f32frombits(0xFF800000u32);
|
||||||
|
if (!chkf32(ninf, "-infinity")) { fail(); };
|
||||||
|
let nan: f32 = math.f32frombits(0x7FC00000u32);
|
||||||
|
if (!chkf32(nan, "nan")) { fail(); };
|
||||||
|
};
|
||||||
|
|
||||||
export fn main() i32 = {
|
export fn main() i32 = {
|
||||||
signalled = 1; f64tos_fixed();
|
signalled = 1; f64tos_fixed();
|
||||||
signalled = 2; f64tos_scientific();
|
signalled = 2; f64tos_scientific();
|
||||||
signalled = 3; f64tos_special();
|
signalled = 3; f64tos_special();
|
||||||
|
signalled = 4; f32tos_fixed();
|
||||||
|
signalled = 5; f32tos_scientific();
|
||||||
|
signalled = 6; f32tos_extremes();
|
||||||
|
signalled = 7; f32tos_special();
|
||||||
os.exit(0);
|
os.exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3322,15 +3322,14 @@ export fn absi64(n: i64) u64 = {
|
|||||||
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
||||||
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
||||||
//
|
//
|
||||||
// SCOPE — the f64tos shortest-representation subset (Hare's ffmt::G,
|
// SCOPE — the f64tos + f32tos shortest-representation subset (Hare's
|
||||||
// prec=void, fflags::NONE). Two deferrals:
|
// ffmt::G, prec=void, fflags::NONE). f32tos (ftos.ha:448) + its f32 Ryū
|
||||||
// - f32tos (ftos.ha:448) + its f32 Ryū sub-path (f32todecf32 +
|
// sub-path (f32todecf32 + mulpow5inv/pow5_divpow2 + mulshift32 + the *32
|
||||||
// mulpow5inv/pow5_divpow2 + mulshift32 + the *32 helpers + the
|
// helpers, reusing the shared u64-core + the f64 SPLIT2 tables — the f32
|
||||||
// F32_POW5_*_BITCOUNT defs) → fold-5b (task #67), gated on the #143
|
// path has no separate tables, matching ftos_ryu.ha) ship here in fold-5b
|
||||||
// f32-arg-push cgen fix: f32tos must call math.f32bits(n), passing
|
// (task #67): the gating #143 f32-arg-push cgen fix landed (aff7725, MOVSS
|
||||||
// an f32 arg, which spills MOVSD (cstage) vs MOVSS (wwstage) →
|
// both stages), so f32tos's math.f32bits(n) call — passing an f32 arg — is
|
||||||
// 990-997 byte-id break. Not isolatable (f32bits IS the reinterpret),
|
// now byte-id-clean. One deferral remains:
|
||||||
// only fixable. f32tos IS a real Hare entry — it ships in #67.
|
|
||||||
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
||||||
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
||||||
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
||||||
@@ -3658,6 +3657,195 @@ fn f64todecf64(mantissa: u64, exponent: u32) decf64 = {
|
|||||||
return decf64 { exponent = (exp: i64), mantissa = output };
|
return decf64 { exponent = (exp: i64), mantissa = output };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==== f32 Ryū sub-path (ftos_ryu.ha). The *32 helpers below mirror their
|
||||||
|
// u64 siblings at 32-bit width; they reuse the SHARED f64computeinvpow5/
|
||||||
|
// f64computepow5 (and thus the f64 SPLIT2 tables) per ftos_ryu.ha — there
|
||||||
|
// is no separate f32 table. Same scalar-PARAM-mutation → copy-to-local,
|
||||||
|
// comma-split, assert → os.assert, expr-yield → block divergences as the
|
||||||
|
// f64 path above. ====
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:52. Largest p with 5^p | value (32-bit).
|
||||||
|
fn pow5fac32(v: u32) u32 = {
|
||||||
|
let value: u32 = v;
|
||||||
|
let count: u32 = 0u32;
|
||||||
|
for (true) {
|
||||||
|
os.assert(value != 0u32, "strconv.pow5fac32: value == 0");
|
||||||
|
let q: u32 = value / 5u32;
|
||||||
|
let r: u32 = value % 5u32;
|
||||||
|
if (r != 0u32) { break; };
|
||||||
|
value = q;
|
||||||
|
count += 1u32;
|
||||||
|
};
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:67.
|
||||||
|
fn pow5multiple32(v: u32, p: u32) bool = { return pow5fac32(v) >= p; };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:75.
|
||||||
|
fn pow2multiple32(v: u32, p: u32) bool = {
|
||||||
|
os.assert(v > 0u32, "strconv.pow2multiple32: v == 0");
|
||||||
|
os.assert(p < 32u32, "strconv.pow2multiple32: p >= 32");
|
||||||
|
return (v & ((1u32 << p) - 1u32)) == 0u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:121. `m * a_lo` etc. carry an explicit
|
||||||
|
// (m: u64) cast (Hare promotes the u32 operand; ww is strict). The bound
|
||||||
|
// assert inlines U32_MAX's value: ww's types.U32_MAX is package-private
|
||||||
|
// (lib/types/types.ww — no `export`), so Hare's `types::U32_MAX` can't be
|
||||||
|
// referenced cross-package.
|
||||||
|
fn mulshift32(m: u32, a: u64, s: u32) u32 = {
|
||||||
|
os.assert(s > 32u32, "strconv.mulshift32: s <= 32");
|
||||||
|
let a_lo: u64 = (a: u32): u64;
|
||||||
|
let a_hi: u64 = a >> 32u64;
|
||||||
|
let b0: u64 = (m: u64) * a_lo;
|
||||||
|
let b1: u64 = (m: u64) * a_hi;
|
||||||
|
let sum: u64 = (b0 >> 32u64) + b1;
|
||||||
|
let ss: u64 = sum >> ((s: u64) - 32u64);
|
||||||
|
os.assert(ss <= 4294967295u64, "strconv.mulshift32: ss > U32_MAX");
|
||||||
|
return ss: u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:130.
|
||||||
|
fn mulpow5inv_divpow2(m: u32, q: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computeinvpow5(q);
|
||||||
|
return mulshift32(m, pow5.1 + 1u64, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:135.
|
||||||
|
fn mulpow5_divpow2(m: u32, i: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computepow5(i);
|
||||||
|
return mulshift32(m, pow5.1, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:387. `exponent` rides i64 not Hare's i32,
|
||||||
|
// for the same reason decf64 does: widening the field to a full second
|
||||||
|
// eightbyte SIDESTEPS the #169 narrow-i32-field struct-return unpack (a
|
||||||
|
// narrow i32 there unpacks MOVL wwstage vs MOVQ cstage). The value always
|
||||||
|
// fits i32 (cast at the init_dec_mant_exp call site). `mantissa` stays u32
|
||||||
|
// (Hare's width); the {u32, pad, i64} layout's first eightbyte holds
|
||||||
|
// mantissa@0 + 4B pad and reads cleanly — byte-id CONFIRMED by the 990-997
|
||||||
|
// gate (0-diff cs vs ww), not relied on as an ABI guarantee.
|
||||||
|
type decf32 = struct { mantissa: u32, exponent: i64 };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:392. Shortest decimal of an f32:
|
||||||
|
// value == mantissa * 10^exponent. `mantissa`/`exponent` are the raw
|
||||||
|
// IEEE-754 fields of an f32.
|
||||||
|
fn f32todecf32(mantissa: u32, exponent: u32) decf32 = {
|
||||||
|
let e2: i32 = (math.F32_EXPONENT_BIAS + math.F32_MANTISSA_BITS + 2u32): i32;
|
||||||
|
let m2: u32 = 0u32;
|
||||||
|
if (exponent == 0u32) {
|
||||||
|
e2 = 1i32 - e2;
|
||||||
|
m2 = mantissa;
|
||||||
|
} else {
|
||||||
|
e2 = (exponent: i32) - e2;
|
||||||
|
m2 = (1u32 << math.F32_MANTISSA_BITS) | mantissa;
|
||||||
|
};
|
||||||
|
let accept_bounds: bool = (m2 & 1u32) == 0u32;
|
||||||
|
let mv: u32 = 4u32 * m2;
|
||||||
|
let mp: u32 = mv + 2u32;
|
||||||
|
let mm_shift: u32 = ibool(mantissa != 0u32 || exponent <= 1u32): u32;
|
||||||
|
let mm: u32 = mv - 1u32 - mm_shift;
|
||||||
|
let vr: u32 = 0u32;
|
||||||
|
let vp: u32 = 0u32;
|
||||||
|
let vm: u32 = 0u32;
|
||||||
|
let e10: i32 = 0i32;
|
||||||
|
let vm_trailing_zeroes: bool = false;
|
||||||
|
let vr_trailing_zeroes: bool = false;
|
||||||
|
let last_removed_digit: u8 = 0u8;
|
||||||
|
if (e2 >= 0i32) {
|
||||||
|
let q: u32 = log10pow2(e2: u32);
|
||||||
|
e10 = q: i32;
|
||||||
|
let k: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q) - 1u32;
|
||||||
|
let i: i32 = -e2 + ((q + k): i32);
|
||||||
|
vr = mulpow5inv_divpow2(mv, q, i);
|
||||||
|
vp = mulpow5inv_divpow2(mp, q, i);
|
||||||
|
vm = mulpow5inv_divpow2(mm, q, i);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
let l: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q - 1u32) - 1u32;
|
||||||
|
// #168 dodge: div/mod on an inline N_CALL result emits a
|
||||||
|
// signed IDIVQ in wwstage; bind the call to a local first.
|
||||||
|
let lrd: u32 = mulpow5inv_divpow2(mv, q - 1u32,
|
||||||
|
-e2 + ((q + l): i32) - 1i32);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 9u32) {
|
||||||
|
if (mv % 5u32 == 0u32) {
|
||||||
|
vr_trailing_zeroes = pow5multiple32(mv, q);
|
||||||
|
} else if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = pow5multiple32(mm, q);
|
||||||
|
} else {
|
||||||
|
vp -= (ibool(pow5multiple32(mp, q)): u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let q: u32 = log10pow5((-e2): u32);
|
||||||
|
e10 = (q: i32) + e2;
|
||||||
|
let i: u32 = (-e2 - (q: i32)): u32;
|
||||||
|
let k: u32 = pow5bits(i) - (F32_POW5_BITCOUNT: u32);
|
||||||
|
let j: i32 = (q: i32) - (k: i32);
|
||||||
|
vr = mulpow5_divpow2(mv, i, j);
|
||||||
|
vp = mulpow5_divpow2(mp, i, j);
|
||||||
|
vm = mulpow5_divpow2(mm, i, j);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
j = (q: i32) - 1i32 - ((pow5bits(i + 1u32): i32) - (F32_POW5_BITCOUNT: i32));
|
||||||
|
// #168 dodge (see above): local-bind before the % 10.
|
||||||
|
let lrd: u32 = mulpow5_divpow2(mv, (i + 1u32), j);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 1u32) {
|
||||||
|
vr_trailing_zeroes = true;
|
||||||
|
if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = mm_shift == 1u32;
|
||||||
|
} else {
|
||||||
|
vp -= 1u32;
|
||||||
|
};
|
||||||
|
} else if (q < 31u32) {
|
||||||
|
vr_trailing_zeroes = pow2multiple32(mv, q - 1u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let removed: i32 = 0i32;
|
||||||
|
let output: u32 = 0u32;
|
||||||
|
if (vm_trailing_zeroes || vr_trailing_zeroes) {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
vm_trailing_zeroes = vm_trailing_zeroes && ((vm - (vm / 10u32) * 10u32) == 0u32);
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
if (vm_trailing_zeroes) {
|
||||||
|
for ((vm % 10u32) == 0u32) {
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (vr_trailing_zeroes && last_removed_digit == 5u8 && vr % 2u32 == 0u32) {
|
||||||
|
last_removed_digit = 4u8; // round to even
|
||||||
|
};
|
||||||
|
let cond1: bool = (vr == vm) && ((!accept_bounds) || (!vm_trailing_zeroes));
|
||||||
|
let cond2: bool = last_removed_digit >= 5u8;
|
||||||
|
output = vr + (ibool(cond1 || cond2): u32);
|
||||||
|
} else {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
output = vr + (ibool(vr == vm || last_removed_digit >= 5u8): u32);
|
||||||
|
};
|
||||||
|
let exp: i32 = e10 + removed;
|
||||||
|
return decf32 { mantissa = output, exponent = (exp: i64) };
|
||||||
|
};
|
||||||
|
|
||||||
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
||||||
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
||||||
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
||||||
@@ -3866,6 +4054,67 @@ export fn f64tos(n: f64) str = {
|
|||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos.ha:448. f32 → shortest base-10 str. Same static-
|
||||||
|
// buffer convention + G/void/NONE-inlined path as f64tos. f32bits(n)
|
||||||
|
// passes an f32 arg → MOVSS both stages post-#143 (aff7725); this is the
|
||||||
|
// piece fold-5b was gated on.
|
||||||
|
//
|
||||||
|
// Hare sizes this [14]u8 (ftos.ha:451: 1 + 1 + 1 + 7 + 1 + 1 + 2). Sized
|
||||||
|
// 32 to reuse f64tos's proven byte-id-clean band: a no-rhs [N]u8 module
|
||||||
|
// buffer at the size-16/24 band emits divergent DATAW counts cs≠ww (#43);
|
||||||
|
// 32 emits 2 DATAW in both. The unused tail bytes are harmless.
|
||||||
|
let f32tos_buf: [32]u8;
|
||||||
|
|
||||||
|
export fn f32tos(n: f32) str = {
|
||||||
|
let bits: u32 = math.f32bits(n);
|
||||||
|
let mantissa: u32 = bits & math.F32_MANTISSA_MASK;
|
||||||
|
let exponent: u32 = (bits >> math.F32_MANTISSA_BITS) & math.F32_EXPONENT_MASK;
|
||||||
|
let sign: bool = (bits >> (math.F32_EXPONENT_BITS + math.F32_MANTISSA_BITS)) > 0u32;
|
||||||
|
let special: bool = exponent == math.F32_EXPONENT_MASK;
|
||||||
|
|
||||||
|
let o: i32 = 0i32;
|
||||||
|
let r: str;
|
||||||
|
r.ptr = &f32tos_buf[0];
|
||||||
|
// NaN carries no sign prefix (ftos.ha:331-333, before sign handling).
|
||||||
|
if (special && mantissa != 0u32) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "nan");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (sign) {
|
||||||
|
f32tos_buf[o] = 45u8; // '-'
|
||||||
|
o += 1i32;
|
||||||
|
};
|
||||||
|
if (special) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "infinity");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (exponent == 0u32 && mantissa == 0u32) {
|
||||||
|
f32tos_buf[o] = 48u8; // '0' (encode_zero, G/void/NONE)
|
||||||
|
o += 1i32;
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = decimal { ... };
|
||||||
|
// *decimal pointer for the field reads (the #170 dodge; see f64tos).
|
||||||
|
let pd: *decimal = &d;
|
||||||
|
let dd: decf32 = f32todecf32(mantissa, exponent);
|
||||||
|
init_dec_mant_exp(pd, (dd.mantissa: u64), (dd.exponent: i32));
|
||||||
|
trim(pd);
|
||||||
|
if (pd.nd == (0u64: size)) {
|
||||||
|
f32tos_buf[o] = 48u8; // rounded to zero
|
||||||
|
o += 1i32;
|
||||||
|
} else if (pd.dp < -1i32 || (pd.dp - (pd.nd: i32)) > 2i32) {
|
||||||
|
o = encode_e_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
} else {
|
||||||
|
o = encode_f_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
};
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
// strconv — Ryū float→string lookup tables + bit-count constants.
|
// strconv — Ryū float→string lookup tables + bit-count constants.
|
||||||
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
||||||
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
||||||
@@ -3893,12 +4142,18 @@ package strconv;
|
|||||||
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
||||||
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
||||||
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
||||||
// The F32_POW5_*_BITCOUNT siblings (ftos_ryu.ha:162-163) land in fold-5b
|
|
||||||
// (task #67) with their only consumer, f32todecf32 — omitted here to keep
|
|
||||||
// this fold dead-code-free.
|
|
||||||
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
||||||
def F64_POW5_BITCOUNT: u8 = 125u8;
|
def F64_POW5_BITCOUNT: u8 = 125u8;
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:162-163. The f32 split-table bit-counts,
|
||||||
|
// derived from the f64 siblings (Hare: F64_..._BITCOUNT - 64). Consumed by
|
||||||
|
// f32todecf32 (ftos.ww), landed in fold-5b (task #67) — the f32 path reuses
|
||||||
|
// the f64 SPLIT2 tables (via f64computeinvpow5/f64computepow5), so no
|
||||||
|
// separate F32 tables exist (matches ftos_ryu.ha). u8 like the f64 defs;
|
||||||
|
// ftos.ww casts to u32/i32 at each use.
|
||||||
|
def F32_POW5_INV_BITCOUNT: u8 = F64_POW5_INV_BITCOUNT - 64u8;
|
||||||
|
def F32_POW5_BITCOUNT: u8 = F64_POW5_BITCOUNT - 64u8;
|
||||||
|
|
||||||
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
||||||
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
||||||
[1u64, 2305843009213693952u64],
|
[1u64, 2305843009213693952u64],
|
||||||
|
|||||||
@@ -1345,15 +1345,14 @@ export fn absi64(n: i64) u64 = {
|
|||||||
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
||||||
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
||||||
//
|
//
|
||||||
// SCOPE — the f64tos shortest-representation subset (Hare's ffmt::G,
|
// SCOPE — the f64tos + f32tos shortest-representation subset (Hare's
|
||||||
// prec=void, fflags::NONE). Two deferrals:
|
// ffmt::G, prec=void, fflags::NONE). f32tos (ftos.ha:448) + its f32 Ryū
|
||||||
// - f32tos (ftos.ha:448) + its f32 Ryū sub-path (f32todecf32 +
|
// sub-path (f32todecf32 + mulpow5inv/pow5_divpow2 + mulshift32 + the *32
|
||||||
// mulpow5inv/pow5_divpow2 + mulshift32 + the *32 helpers + the
|
// helpers, reusing the shared u64-core + the f64 SPLIT2 tables — the f32
|
||||||
// F32_POW5_*_BITCOUNT defs) → fold-5b (task #67), gated on the #143
|
// path has no separate tables, matching ftos_ryu.ha) ship here in fold-5b
|
||||||
// f32-arg-push cgen fix: f32tos must call math.f32bits(n), passing
|
// (task #67): the gating #143 f32-arg-push cgen fix landed (aff7725, MOVSS
|
||||||
// an f32 arg, which spills MOVSD (cstage) vs MOVSS (wwstage) →
|
// both stages), so f32tos's math.f32bits(n) call — passing an f32 arg — is
|
||||||
// 990-997 byte-id break. Not isolatable (f32bits IS the reinterpret),
|
// now byte-id-clean. One deferral remains:
|
||||||
// only fixable. f32tos IS a real Hare entry — it ships in #67.
|
|
||||||
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
||||||
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
||||||
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
||||||
@@ -1681,6 +1680,195 @@ fn f64todecf64(mantissa: u64, exponent: u32) decf64 = {
|
|||||||
return decf64 { exponent = (exp: i64), mantissa = output };
|
return decf64 { exponent = (exp: i64), mantissa = output };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==== f32 Ryū sub-path (ftos_ryu.ha). The *32 helpers below mirror their
|
||||||
|
// u64 siblings at 32-bit width; they reuse the SHARED f64computeinvpow5/
|
||||||
|
// f64computepow5 (and thus the f64 SPLIT2 tables) per ftos_ryu.ha — there
|
||||||
|
// is no separate f32 table. Same scalar-PARAM-mutation → copy-to-local,
|
||||||
|
// comma-split, assert → os.assert, expr-yield → block divergences as the
|
||||||
|
// f64 path above. ====
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:52. Largest p with 5^p | value (32-bit).
|
||||||
|
fn pow5fac32(v: u32) u32 = {
|
||||||
|
let value: u32 = v;
|
||||||
|
let count: u32 = 0u32;
|
||||||
|
for (true) {
|
||||||
|
os.assert(value != 0u32, "strconv.pow5fac32: value == 0");
|
||||||
|
let q: u32 = value / 5u32;
|
||||||
|
let r: u32 = value % 5u32;
|
||||||
|
if (r != 0u32) { break; };
|
||||||
|
value = q;
|
||||||
|
count += 1u32;
|
||||||
|
};
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:67.
|
||||||
|
fn pow5multiple32(v: u32, p: u32) bool = { return pow5fac32(v) >= p; };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:75.
|
||||||
|
fn pow2multiple32(v: u32, p: u32) bool = {
|
||||||
|
os.assert(v > 0u32, "strconv.pow2multiple32: v == 0");
|
||||||
|
os.assert(p < 32u32, "strconv.pow2multiple32: p >= 32");
|
||||||
|
return (v & ((1u32 << p) - 1u32)) == 0u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:121. `m * a_lo` etc. carry an explicit
|
||||||
|
// (m: u64) cast (Hare promotes the u32 operand; ww is strict). The bound
|
||||||
|
// assert inlines U32_MAX's value: ww's types.U32_MAX is package-private
|
||||||
|
// (lib/types/types.ww — no `export`), so Hare's `types::U32_MAX` can't be
|
||||||
|
// referenced cross-package.
|
||||||
|
fn mulshift32(m: u32, a: u64, s: u32) u32 = {
|
||||||
|
os.assert(s > 32u32, "strconv.mulshift32: s <= 32");
|
||||||
|
let a_lo: u64 = (a: u32): u64;
|
||||||
|
let a_hi: u64 = a >> 32u64;
|
||||||
|
let b0: u64 = (m: u64) * a_lo;
|
||||||
|
let b1: u64 = (m: u64) * a_hi;
|
||||||
|
let sum: u64 = (b0 >> 32u64) + b1;
|
||||||
|
let ss: u64 = sum >> ((s: u64) - 32u64);
|
||||||
|
os.assert(ss <= 4294967295u64, "strconv.mulshift32: ss > U32_MAX");
|
||||||
|
return ss: u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:130.
|
||||||
|
fn mulpow5inv_divpow2(m: u32, q: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computeinvpow5(q);
|
||||||
|
return mulshift32(m, pow5.1 + 1u64, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:135.
|
||||||
|
fn mulpow5_divpow2(m: u32, i: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computepow5(i);
|
||||||
|
return mulshift32(m, pow5.1, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:387. `exponent` rides i64 not Hare's i32,
|
||||||
|
// for the same reason decf64 does: widening the field to a full second
|
||||||
|
// eightbyte SIDESTEPS the #169 narrow-i32-field struct-return unpack (a
|
||||||
|
// narrow i32 there unpacks MOVL wwstage vs MOVQ cstage). The value always
|
||||||
|
// fits i32 (cast at the init_dec_mant_exp call site). `mantissa` stays u32
|
||||||
|
// (Hare's width); the {u32, pad, i64} layout's first eightbyte holds
|
||||||
|
// mantissa@0 + 4B pad and reads cleanly — byte-id CONFIRMED by the 990-997
|
||||||
|
// gate (0-diff cs vs ww), not relied on as an ABI guarantee.
|
||||||
|
type decf32 = struct { mantissa: u32, exponent: i64 };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:392. Shortest decimal of an f32:
|
||||||
|
// value == mantissa * 10^exponent. `mantissa`/`exponent` are the raw
|
||||||
|
// IEEE-754 fields of an f32.
|
||||||
|
fn f32todecf32(mantissa: u32, exponent: u32) decf32 = {
|
||||||
|
let e2: i32 = (math.F32_EXPONENT_BIAS + math.F32_MANTISSA_BITS + 2u32): i32;
|
||||||
|
let m2: u32 = 0u32;
|
||||||
|
if (exponent == 0u32) {
|
||||||
|
e2 = 1i32 - e2;
|
||||||
|
m2 = mantissa;
|
||||||
|
} else {
|
||||||
|
e2 = (exponent: i32) - e2;
|
||||||
|
m2 = (1u32 << math.F32_MANTISSA_BITS) | mantissa;
|
||||||
|
};
|
||||||
|
let accept_bounds: bool = (m2 & 1u32) == 0u32;
|
||||||
|
let mv: u32 = 4u32 * m2;
|
||||||
|
let mp: u32 = mv + 2u32;
|
||||||
|
let mm_shift: u32 = ibool(mantissa != 0u32 || exponent <= 1u32): u32;
|
||||||
|
let mm: u32 = mv - 1u32 - mm_shift;
|
||||||
|
let vr: u32 = 0u32;
|
||||||
|
let vp: u32 = 0u32;
|
||||||
|
let vm: u32 = 0u32;
|
||||||
|
let e10: i32 = 0i32;
|
||||||
|
let vm_trailing_zeroes: bool = false;
|
||||||
|
let vr_trailing_zeroes: bool = false;
|
||||||
|
let last_removed_digit: u8 = 0u8;
|
||||||
|
if (e2 >= 0i32) {
|
||||||
|
let q: u32 = log10pow2(e2: u32);
|
||||||
|
e10 = q: i32;
|
||||||
|
let k: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q) - 1u32;
|
||||||
|
let i: i32 = -e2 + ((q + k): i32);
|
||||||
|
vr = mulpow5inv_divpow2(mv, q, i);
|
||||||
|
vp = mulpow5inv_divpow2(mp, q, i);
|
||||||
|
vm = mulpow5inv_divpow2(mm, q, i);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
let l: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q - 1u32) - 1u32;
|
||||||
|
// #168 dodge: div/mod on an inline N_CALL result emits a
|
||||||
|
// signed IDIVQ in wwstage; bind the call to a local first.
|
||||||
|
let lrd: u32 = mulpow5inv_divpow2(mv, q - 1u32,
|
||||||
|
-e2 + ((q + l): i32) - 1i32);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 9u32) {
|
||||||
|
if (mv % 5u32 == 0u32) {
|
||||||
|
vr_trailing_zeroes = pow5multiple32(mv, q);
|
||||||
|
} else if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = pow5multiple32(mm, q);
|
||||||
|
} else {
|
||||||
|
vp -= (ibool(pow5multiple32(mp, q)): u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let q: u32 = log10pow5((-e2): u32);
|
||||||
|
e10 = (q: i32) + e2;
|
||||||
|
let i: u32 = (-e2 - (q: i32)): u32;
|
||||||
|
let k: u32 = pow5bits(i) - (F32_POW5_BITCOUNT: u32);
|
||||||
|
let j: i32 = (q: i32) - (k: i32);
|
||||||
|
vr = mulpow5_divpow2(mv, i, j);
|
||||||
|
vp = mulpow5_divpow2(mp, i, j);
|
||||||
|
vm = mulpow5_divpow2(mm, i, j);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
j = (q: i32) - 1i32 - ((pow5bits(i + 1u32): i32) - (F32_POW5_BITCOUNT: i32));
|
||||||
|
// #168 dodge (see above): local-bind before the % 10.
|
||||||
|
let lrd: u32 = mulpow5_divpow2(mv, (i + 1u32), j);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 1u32) {
|
||||||
|
vr_trailing_zeroes = true;
|
||||||
|
if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = mm_shift == 1u32;
|
||||||
|
} else {
|
||||||
|
vp -= 1u32;
|
||||||
|
};
|
||||||
|
} else if (q < 31u32) {
|
||||||
|
vr_trailing_zeroes = pow2multiple32(mv, q - 1u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let removed: i32 = 0i32;
|
||||||
|
let output: u32 = 0u32;
|
||||||
|
if (vm_trailing_zeroes || vr_trailing_zeroes) {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
vm_trailing_zeroes = vm_trailing_zeroes && ((vm - (vm / 10u32) * 10u32) == 0u32);
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
if (vm_trailing_zeroes) {
|
||||||
|
for ((vm % 10u32) == 0u32) {
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (vr_trailing_zeroes && last_removed_digit == 5u8 && vr % 2u32 == 0u32) {
|
||||||
|
last_removed_digit = 4u8; // round to even
|
||||||
|
};
|
||||||
|
let cond1: bool = (vr == vm) && ((!accept_bounds) || (!vm_trailing_zeroes));
|
||||||
|
let cond2: bool = last_removed_digit >= 5u8;
|
||||||
|
output = vr + (ibool(cond1 || cond2): u32);
|
||||||
|
} else {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
output = vr + (ibool(vr == vm || last_removed_digit >= 5u8): u32);
|
||||||
|
};
|
||||||
|
let exp: i32 = e10 + removed;
|
||||||
|
return decf32 { mantissa = output, exponent = (exp: i64) };
|
||||||
|
};
|
||||||
|
|
||||||
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
||||||
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
||||||
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
||||||
@@ -1889,6 +2077,67 @@ export fn f64tos(n: f64) str = {
|
|||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos.ha:448. f32 → shortest base-10 str. Same static-
|
||||||
|
// buffer convention + G/void/NONE-inlined path as f64tos. f32bits(n)
|
||||||
|
// passes an f32 arg → MOVSS both stages post-#143 (aff7725); this is the
|
||||||
|
// piece fold-5b was gated on.
|
||||||
|
//
|
||||||
|
// Hare sizes this [14]u8 (ftos.ha:451: 1 + 1 + 1 + 7 + 1 + 1 + 2). Sized
|
||||||
|
// 32 to reuse f64tos's proven byte-id-clean band: a no-rhs [N]u8 module
|
||||||
|
// buffer at the size-16/24 band emits divergent DATAW counts cs≠ww (#43);
|
||||||
|
// 32 emits 2 DATAW in both. The unused tail bytes are harmless.
|
||||||
|
let f32tos_buf: [32]u8;
|
||||||
|
|
||||||
|
export fn f32tos(n: f32) str = {
|
||||||
|
let bits: u32 = math.f32bits(n);
|
||||||
|
let mantissa: u32 = bits & math.F32_MANTISSA_MASK;
|
||||||
|
let exponent: u32 = (bits >> math.F32_MANTISSA_BITS) & math.F32_EXPONENT_MASK;
|
||||||
|
let sign: bool = (bits >> (math.F32_EXPONENT_BITS + math.F32_MANTISSA_BITS)) > 0u32;
|
||||||
|
let special: bool = exponent == math.F32_EXPONENT_MASK;
|
||||||
|
|
||||||
|
let o: i32 = 0i32;
|
||||||
|
let r: str;
|
||||||
|
r.ptr = &f32tos_buf[0];
|
||||||
|
// NaN carries no sign prefix (ftos.ha:331-333, before sign handling).
|
||||||
|
if (special && mantissa != 0u32) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "nan");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (sign) {
|
||||||
|
f32tos_buf[o] = 45u8; // '-'
|
||||||
|
o += 1i32;
|
||||||
|
};
|
||||||
|
if (special) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "infinity");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (exponent == 0u32 && mantissa == 0u32) {
|
||||||
|
f32tos_buf[o] = 48u8; // '0' (encode_zero, G/void/NONE)
|
||||||
|
o += 1i32;
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = decimal { ... };
|
||||||
|
// *decimal pointer for the field reads (the #170 dodge; see f64tos).
|
||||||
|
let pd: *decimal = &d;
|
||||||
|
let dd: decf32 = f32todecf32(mantissa, exponent);
|
||||||
|
init_dec_mant_exp(pd, (dd.mantissa: u64), (dd.exponent: i32));
|
||||||
|
trim(pd);
|
||||||
|
if (pd.nd == (0u64: size)) {
|
||||||
|
f32tos_buf[o] = 48u8; // rounded to zero
|
||||||
|
o += 1i32;
|
||||||
|
} else if (pd.dp < -1i32 || (pd.dp - (pd.nd: i32)) > 2i32) {
|
||||||
|
o = encode_e_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
} else {
|
||||||
|
o = encode_f_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
};
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
// strconv — Ryū float→string lookup tables + bit-count constants.
|
// strconv — Ryū float→string lookup tables + bit-count constants.
|
||||||
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
||||||
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
||||||
@@ -1916,12 +2165,18 @@ package strconv;
|
|||||||
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
||||||
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
||||||
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
||||||
// The F32_POW5_*_BITCOUNT siblings (ftos_ryu.ha:162-163) land in fold-5b
|
|
||||||
// (task #67) with their only consumer, f32todecf32 — omitted here to keep
|
|
||||||
// this fold dead-code-free.
|
|
||||||
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
||||||
def F64_POW5_BITCOUNT: u8 = 125u8;
|
def F64_POW5_BITCOUNT: u8 = 125u8;
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:162-163. The f32 split-table bit-counts,
|
||||||
|
// derived from the f64 siblings (Hare: F64_..._BITCOUNT - 64). Consumed by
|
||||||
|
// f32todecf32 (ftos.ww), landed in fold-5b (task #67) — the f32 path reuses
|
||||||
|
// the f64 SPLIT2 tables (via f64computeinvpow5/f64computepow5), so no
|
||||||
|
// separate F32 tables exist (matches ftos_ryu.ha). u8 like the f64 defs;
|
||||||
|
// ftos.ww casts to u32/i32 at each use.
|
||||||
|
def F32_POW5_INV_BITCOUNT: u8 = F64_POW5_INV_BITCOUNT - 64u8;
|
||||||
|
def F32_POW5_BITCOUNT: u8 = F64_POW5_BITCOUNT - 64u8;
|
||||||
|
|
||||||
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
||||||
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
||||||
[1u64, 2305843009213693952u64],
|
[1u64, 2305843009213693952u64],
|
||||||
|
|||||||
@@ -1345,15 +1345,14 @@ export fn absi64(n: i64) u64 = {
|
|||||||
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
// https://doi.org/10.1145/3192366.3192369 — Hare translated it from the
|
||||||
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
// reference C (https://github.com/ulfjack/ryu); ww follows Hare.
|
||||||
//
|
//
|
||||||
// SCOPE — the f64tos shortest-representation subset (Hare's ffmt::G,
|
// SCOPE — the f64tos + f32tos shortest-representation subset (Hare's
|
||||||
// prec=void, fflags::NONE). Two deferrals:
|
// ffmt::G, prec=void, fflags::NONE). f32tos (ftos.ha:448) + its f32 Ryū
|
||||||
// - f32tos (ftos.ha:448) + its f32 Ryū sub-path (f32todecf32 +
|
// sub-path (f32todecf32 + mulpow5inv/pow5_divpow2 + mulshift32 + the *32
|
||||||
// mulpow5inv/pow5_divpow2 + mulshift32 + the *32 helpers + the
|
// helpers, reusing the shared u64-core + the f64 SPLIT2 tables — the f32
|
||||||
// F32_POW5_*_BITCOUNT defs) → fold-5b (task #67), gated on the #143
|
// path has no separate tables, matching ftos_ryu.ha) ship here in fold-5b
|
||||||
// f32-arg-push cgen fix: f32tos must call math.f32bits(n), passing
|
// (task #67): the gating #143 f32-arg-push cgen fix landed (aff7725, MOVSS
|
||||||
// an f32 arg, which spills MOVSD (cstage) vs MOVSS (wwstage) →
|
// both stages), so f32tos's math.f32bits(n) call — passing an f32 arg — is
|
||||||
// 990-997 byte-id break. Not isolatable (f32bits IS the reinterpret),
|
// now byte-id-clean. One deferral remains:
|
||||||
// only fixable. f32tos IS a real Hare entry — it ships in #67.
|
|
||||||
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
// - the parametric fftosf/ffmt/fflags/ftosf surface → task #64 (needs
|
||||||
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
// io::handle/memio + a `(size|io::error)?` per appendrune (#158);
|
||||||
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
// for G/void/NONE the ffmt/fflags/precision/multiprecision-fallback
|
||||||
@@ -1681,6 +1680,195 @@ fn f64todecf64(mantissa: u64, exponent: u32) decf64 = {
|
|||||||
return decf64 { exponent = (exp: i64), mantissa = output };
|
return decf64 { exponent = (exp: i64), mantissa = output };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==== f32 Ryū sub-path (ftos_ryu.ha). The *32 helpers below mirror their
|
||||||
|
// u64 siblings at 32-bit width; they reuse the SHARED f64computeinvpow5/
|
||||||
|
// f64computepow5 (and thus the f64 SPLIT2 tables) per ftos_ryu.ha — there
|
||||||
|
// is no separate f32 table. Same scalar-PARAM-mutation → copy-to-local,
|
||||||
|
// comma-split, assert → os.assert, expr-yield → block divergences as the
|
||||||
|
// f64 path above. ====
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:52. Largest p with 5^p | value (32-bit).
|
||||||
|
fn pow5fac32(v: u32) u32 = {
|
||||||
|
let value: u32 = v;
|
||||||
|
let count: u32 = 0u32;
|
||||||
|
for (true) {
|
||||||
|
os.assert(value != 0u32, "strconv.pow5fac32: value == 0");
|
||||||
|
let q: u32 = value / 5u32;
|
||||||
|
let r: u32 = value % 5u32;
|
||||||
|
if (r != 0u32) { break; };
|
||||||
|
value = q;
|
||||||
|
count += 1u32;
|
||||||
|
};
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:67.
|
||||||
|
fn pow5multiple32(v: u32, p: u32) bool = { return pow5fac32(v) >= p; };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:75.
|
||||||
|
fn pow2multiple32(v: u32, p: u32) bool = {
|
||||||
|
os.assert(v > 0u32, "strconv.pow2multiple32: v == 0");
|
||||||
|
os.assert(p < 32u32, "strconv.pow2multiple32: p >= 32");
|
||||||
|
return (v & ((1u32 << p) - 1u32)) == 0u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:121. `m * a_lo` etc. carry an explicit
|
||||||
|
// (m: u64) cast (Hare promotes the u32 operand; ww is strict). The bound
|
||||||
|
// assert inlines U32_MAX's value: ww's types.U32_MAX is package-private
|
||||||
|
// (lib/types/types.ww — no `export`), so Hare's `types::U32_MAX` can't be
|
||||||
|
// referenced cross-package.
|
||||||
|
fn mulshift32(m: u32, a: u64, s: u32) u32 = {
|
||||||
|
os.assert(s > 32u32, "strconv.mulshift32: s <= 32");
|
||||||
|
let a_lo: u64 = (a: u32): u64;
|
||||||
|
let a_hi: u64 = a >> 32u64;
|
||||||
|
let b0: u64 = (m: u64) * a_lo;
|
||||||
|
let b1: u64 = (m: u64) * a_hi;
|
||||||
|
let sum: u64 = (b0 >> 32u64) + b1;
|
||||||
|
let ss: u64 = sum >> ((s: u64) - 32u64);
|
||||||
|
os.assert(ss <= 4294967295u64, "strconv.mulshift32: ss > U32_MAX");
|
||||||
|
return ss: u32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:130.
|
||||||
|
fn mulpow5inv_divpow2(m: u32, q: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computeinvpow5(q);
|
||||||
|
return mulshift32(m, pow5.1 + 1u64, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:135.
|
||||||
|
fn mulpow5_divpow2(m: u32, i: u32, j: i32) u32 = {
|
||||||
|
let pow5 = f64computepow5(i);
|
||||||
|
return mulshift32(m, pow5.1, (j: u32));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:387. `exponent` rides i64 not Hare's i32,
|
||||||
|
// for the same reason decf64 does: widening the field to a full second
|
||||||
|
// eightbyte SIDESTEPS the #169 narrow-i32-field struct-return unpack (a
|
||||||
|
// narrow i32 there unpacks MOVL wwstage vs MOVQ cstage). The value always
|
||||||
|
// fits i32 (cast at the init_dec_mant_exp call site). `mantissa` stays u32
|
||||||
|
// (Hare's width); the {u32, pad, i64} layout's first eightbyte holds
|
||||||
|
// mantissa@0 + 4B pad and reads cleanly — byte-id CONFIRMED by the 990-997
|
||||||
|
// gate (0-diff cs vs ww), not relied on as an ABI guarantee.
|
||||||
|
type decf32 = struct { mantissa: u32, exponent: i64 };
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:392. Shortest decimal of an f32:
|
||||||
|
// value == mantissa * 10^exponent. `mantissa`/`exponent` are the raw
|
||||||
|
// IEEE-754 fields of an f32.
|
||||||
|
fn f32todecf32(mantissa: u32, exponent: u32) decf32 = {
|
||||||
|
let e2: i32 = (math.F32_EXPONENT_BIAS + math.F32_MANTISSA_BITS + 2u32): i32;
|
||||||
|
let m2: u32 = 0u32;
|
||||||
|
if (exponent == 0u32) {
|
||||||
|
e2 = 1i32 - e2;
|
||||||
|
m2 = mantissa;
|
||||||
|
} else {
|
||||||
|
e2 = (exponent: i32) - e2;
|
||||||
|
m2 = (1u32 << math.F32_MANTISSA_BITS) | mantissa;
|
||||||
|
};
|
||||||
|
let accept_bounds: bool = (m2 & 1u32) == 0u32;
|
||||||
|
let mv: u32 = 4u32 * m2;
|
||||||
|
let mp: u32 = mv + 2u32;
|
||||||
|
let mm_shift: u32 = ibool(mantissa != 0u32 || exponent <= 1u32): u32;
|
||||||
|
let mm: u32 = mv - 1u32 - mm_shift;
|
||||||
|
let vr: u32 = 0u32;
|
||||||
|
let vp: u32 = 0u32;
|
||||||
|
let vm: u32 = 0u32;
|
||||||
|
let e10: i32 = 0i32;
|
||||||
|
let vm_trailing_zeroes: bool = false;
|
||||||
|
let vr_trailing_zeroes: bool = false;
|
||||||
|
let last_removed_digit: u8 = 0u8;
|
||||||
|
if (e2 >= 0i32) {
|
||||||
|
let q: u32 = log10pow2(e2: u32);
|
||||||
|
e10 = q: i32;
|
||||||
|
let k: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q) - 1u32;
|
||||||
|
let i: i32 = -e2 + ((q + k): i32);
|
||||||
|
vr = mulpow5inv_divpow2(mv, q, i);
|
||||||
|
vp = mulpow5inv_divpow2(mp, q, i);
|
||||||
|
vm = mulpow5inv_divpow2(mm, q, i);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
let l: u32 = (F32_POW5_INV_BITCOUNT: u32) + pow5bits(q - 1u32) - 1u32;
|
||||||
|
// #168 dodge: div/mod on an inline N_CALL result emits a
|
||||||
|
// signed IDIVQ in wwstage; bind the call to a local first.
|
||||||
|
let lrd: u32 = mulpow5inv_divpow2(mv, q - 1u32,
|
||||||
|
-e2 + ((q + l): i32) - 1i32);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 9u32) {
|
||||||
|
if (mv % 5u32 == 0u32) {
|
||||||
|
vr_trailing_zeroes = pow5multiple32(mv, q);
|
||||||
|
} else if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = pow5multiple32(mm, q);
|
||||||
|
} else {
|
||||||
|
vp -= (ibool(pow5multiple32(mp, q)): u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let q: u32 = log10pow5((-e2): u32);
|
||||||
|
e10 = (q: i32) + e2;
|
||||||
|
let i: u32 = (-e2 - (q: i32)): u32;
|
||||||
|
let k: u32 = pow5bits(i) - (F32_POW5_BITCOUNT: u32);
|
||||||
|
let j: i32 = (q: i32) - (k: i32);
|
||||||
|
vr = mulpow5_divpow2(mv, i, j);
|
||||||
|
vp = mulpow5_divpow2(mp, i, j);
|
||||||
|
vm = mulpow5_divpow2(mm, i, j);
|
||||||
|
if (q != 0u32 && (vp - 1u32) / 10u32 <= vm / 10u32) {
|
||||||
|
j = (q: i32) - 1i32 - ((pow5bits(i + 1u32): i32) - (F32_POW5_BITCOUNT: i32));
|
||||||
|
// #168 dodge (see above): local-bind before the % 10.
|
||||||
|
let lrd: u32 = mulpow5_divpow2(mv, (i + 1u32), j);
|
||||||
|
last_removed_digit = (lrd % 10u32): u8;
|
||||||
|
};
|
||||||
|
if (q <= 1u32) {
|
||||||
|
vr_trailing_zeroes = true;
|
||||||
|
if (accept_bounds) {
|
||||||
|
vm_trailing_zeroes = mm_shift == 1u32;
|
||||||
|
} else {
|
||||||
|
vp -= 1u32;
|
||||||
|
};
|
||||||
|
} else if (q < 31u32) {
|
||||||
|
vr_trailing_zeroes = pow2multiple32(mv, q - 1u32);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let removed: i32 = 0i32;
|
||||||
|
let output: u32 = 0u32;
|
||||||
|
if (vm_trailing_zeroes || vr_trailing_zeroes) {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
vm_trailing_zeroes = vm_trailing_zeroes && ((vm - (vm / 10u32) * 10u32) == 0u32);
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
if (vm_trailing_zeroes) {
|
||||||
|
for ((vm % 10u32) == 0u32) {
|
||||||
|
vr_trailing_zeroes = vr_trailing_zeroes && (last_removed_digit == 0u8);
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (vr_trailing_zeroes && last_removed_digit == 5u8 && vr % 2u32 == 0u32) {
|
||||||
|
last_removed_digit = 4u8; // round to even
|
||||||
|
};
|
||||||
|
let cond1: bool = (vr == vm) && ((!accept_bounds) || (!vm_trailing_zeroes));
|
||||||
|
let cond2: bool = last_removed_digit >= 5u8;
|
||||||
|
output = vr + (ibool(cond1 || cond2): u32);
|
||||||
|
} else {
|
||||||
|
for ((vp / 10u32) > (vm / 10u32)) {
|
||||||
|
last_removed_digit = (vr % 10u32): u8;
|
||||||
|
vr /= 10u32;
|
||||||
|
vp /= 10u32;
|
||||||
|
vm /= 10u32;
|
||||||
|
removed += 1i32;
|
||||||
|
};
|
||||||
|
output = vr + (ibool(vr == vm || last_removed_digit >= 5u8): u32);
|
||||||
|
};
|
||||||
|
let exp: i32 = e10 + removed;
|
||||||
|
return decf32 { mantissa = output, exponent = (exp: i64) };
|
||||||
|
};
|
||||||
|
|
||||||
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
// ==== G-format encode layer (ftos.ha) — only the ffmt::G / prec=void /
|
||||||
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
// fflags::NONE-REACHABLE logic. The SHOW_POINT/precision/E-vs-uppercase
|
||||||
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
// arms (ftos.ha:88-105, 127-145, 170-213's zeros/caps) are UNREACHABLE
|
||||||
@@ -1889,6 +2077,67 @@ export fn f64tos(n: f64) str = {
|
|||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos.ha:448. f32 → shortest base-10 str. Same static-
|
||||||
|
// buffer convention + G/void/NONE-inlined path as f64tos. f32bits(n)
|
||||||
|
// passes an f32 arg → MOVSS both stages post-#143 (aff7725); this is the
|
||||||
|
// piece fold-5b was gated on.
|
||||||
|
//
|
||||||
|
// Hare sizes this [14]u8 (ftos.ha:451: 1 + 1 + 1 + 7 + 1 + 1 + 2). Sized
|
||||||
|
// 32 to reuse f64tos's proven byte-id-clean band: a no-rhs [N]u8 module
|
||||||
|
// buffer at the size-16/24 band emits divergent DATAW counts cs≠ww (#43);
|
||||||
|
// 32 emits 2 DATAW in both. The unused tail bytes are harmless.
|
||||||
|
let f32tos_buf: [32]u8;
|
||||||
|
|
||||||
|
export fn f32tos(n: f32) str = {
|
||||||
|
let bits: u32 = math.f32bits(n);
|
||||||
|
let mantissa: u32 = bits & math.F32_MANTISSA_MASK;
|
||||||
|
let exponent: u32 = (bits >> math.F32_MANTISSA_BITS) & math.F32_EXPONENT_MASK;
|
||||||
|
let sign: bool = (bits >> (math.F32_EXPONENT_BITS + math.F32_MANTISSA_BITS)) > 0u32;
|
||||||
|
let special: bool = exponent == math.F32_EXPONENT_MASK;
|
||||||
|
|
||||||
|
let o: i32 = 0i32;
|
||||||
|
let r: str;
|
||||||
|
r.ptr = &f32tos_buf[0];
|
||||||
|
// NaN carries no sign prefix (ftos.ha:331-333, before sign handling).
|
||||||
|
if (special && mantissa != 0u32) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "nan");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (sign) {
|
||||||
|
f32tos_buf[o] = 45u8; // '-'
|
||||||
|
o += 1i32;
|
||||||
|
};
|
||||||
|
if (special) {
|
||||||
|
o = putstr(f32tos_buf[0:32], o, "infinity");
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
if (exponent == 0u32 && mantissa == 0u32) {
|
||||||
|
f32tos_buf[o] = 48u8; // '0' (encode_zero, G/void/NONE)
|
||||||
|
o += 1i32;
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = decimal { ... };
|
||||||
|
// *decimal pointer for the field reads (the #170 dodge; see f64tos).
|
||||||
|
let pd: *decimal = &d;
|
||||||
|
let dd: decf32 = f32todecf32(mantissa, exponent);
|
||||||
|
init_dec_mant_exp(pd, (dd.mantissa: u64), (dd.exponent: i32));
|
||||||
|
trim(pd);
|
||||||
|
if (pd.nd == (0u64: size)) {
|
||||||
|
f32tos_buf[o] = 48u8; // rounded to zero
|
||||||
|
o += 1i32;
|
||||||
|
} else if (pd.dp < -1i32 || (pd.dp - (pd.nd: i32)) > 2i32) {
|
||||||
|
o = encode_e_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
} else {
|
||||||
|
o = encode_f_dec(pd, f32tos_buf[0:32], o);
|
||||||
|
};
|
||||||
|
r.len = o;
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
// strconv — Ryū float→string lookup tables + bit-count constants.
|
// strconv — Ryū float→string lookup tables + bit-count constants.
|
||||||
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
// Mirrors ref/hare/strconv/ftos_ryu.ha:159-222 byte-exact. Pure data
|
||||||
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
// fold (strconv #106 fold-5): no logic, consumed by ftos.ww's
|
||||||
@@ -1916,12 +2165,18 @@ package strconv;
|
|||||||
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
// power-of-five tables. Defined u8 (faithful); ftos.ww casts to u32/i32
|
||||||
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
// at each use site (Hare promotes a u8 def inside mixed-width arithmetic;
|
||||||
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
// ww is strict — explicit cast, project_int_machine_word_derived_limits).
|
||||||
// The F32_POW5_*_BITCOUNT siblings (ftos_ryu.ha:162-163) land in fold-5b
|
|
||||||
// (task #67) with their only consumer, f32todecf32 — omitted here to keep
|
|
||||||
// this fold dead-code-free.
|
|
||||||
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
def F64_POW5_INV_BITCOUNT: u8 = 125u8;
|
||||||
def F64_POW5_BITCOUNT: u8 = 125u8;
|
def F64_POW5_BITCOUNT: u8 = 125u8;
|
||||||
|
|
||||||
|
// ref/hare/strconv/ftos_ryu.ha:162-163. The f32 split-table bit-counts,
|
||||||
|
// derived from the f64 siblings (Hare: F64_..._BITCOUNT - 64). Consumed by
|
||||||
|
// f32todecf32 (ftos.ww), landed in fold-5b (task #67) — the f32 path reuses
|
||||||
|
// the f64 SPLIT2 tables (via f64computeinvpow5/f64computepow5), so no
|
||||||
|
// separate F32 tables exist (matches ftos_ryu.ha). u8 like the f64 defs;
|
||||||
|
// ftos.ww casts to u32/i32 at each use.
|
||||||
|
def F32_POW5_INV_BITCOUNT: u8 = F64_POW5_INV_BITCOUNT - 64u8;
|
||||||
|
def F32_POW5_BITCOUNT: u8 = F64_POW5_BITCOUNT - 64u8;
|
||||||
|
|
||||||
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
// ref/hare/strconv/ftos_ryu.ha:165-181.
|
||||||
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
let F64_POW5_INV_SPLIT2: [15][2]u64 = [
|
||||||
[1u64, 2305843009213693952u64],
|
[1u64, 2305843009213693952u64],
|
||||||
|
|||||||
Reference in New Issue
Block a user