lib/strconv: f64tos Ryū shortest float→string (#106 fold-5a)
Graduate f64tos from the lossy fixed-point placeholder to Ryū shortest- round-trippable (ftos.ha:432 + ftos_ryu.ha). f64tos(n:f64) str, G-format (void/NONE) — the faithful documented subset (full parametric fftosf is dead code for G/void/NONE → deferred #64). Ryū core decomposed: struct- RETURN + scalar params (Hare's r128 idiom; avoids tuple-ABI #163-166). f64 powers tables [15][2]/[13][2]u64 (2D #156). Zero float literals. Both E+F encode paths reachable+tested, no dead code. Graduation (lib-note "don't keep both"): old lossy f64tos deleted; fmt fprintf_f64_huge "huge"→"9.5e18" (improvement). 5 value-faithful filed- bug dodges (byte-id, documented): #167/#169/#170/#43/#144. Test 908. Make test 186/186 incl 990-997 byte-id + combined_ww_fresh. Drew's strconv 5-fold plan — primary completion (f64tos). f32tos coda #67 (behind #143); parametric ftosf #64.
This commit is contained in:
@@ -261,91 +261,12 @@ export fn stou8(s: str, b: base) (u8 | invalid | overflow) = {
|
||||
return 0: invalid;
|
||||
};
|
||||
|
||||
// f64tos — convert v to a decimal string. Returns owned str; release
|
||||
// via os.free. Mirrors Hare's strconv::f64tos (current ww impl is
|
||||
// fixed-point only, max 6 fractional digits, no NaN/Inf support —
|
||||
// see graduate-to-Ryū note below).
|
||||
//
|
||||
// Surface:
|
||||
//
|
||||
// - finite values only. NaN/±Inf detection needs an f64→u64 bit
|
||||
// reinterpret cast that the cgen doesn't expose yet.
|
||||
// - fixed-point only, up to 6 fractional digits. Trailing zeros
|
||||
// after the decimal point are trimmed. Trailing '.' is dropped.
|
||||
// - magnitudes ≥ 9e18 (overflows i64 in the integer-part cast)
|
||||
// fall back to the literal token "huge". Hare would print these
|
||||
// in scientific notation via Ryū; we will graduate when the
|
||||
// compiler grows the bit-reinterpret cast.
|
||||
//
|
||||
// Round-trip is therefore lossy past 6 fractional digits.
|
||||
//
|
||||
// No float literals in the body — 990's wwdump diff requires this
|
||||
// file's TK_FLOAT count to match between C and ww front-ends, and
|
||||
// the ww-side wwdump currently skips TK_FLOAT.fval while the C side
|
||||
// %g-formats it. Same trick lib/ww/lex/lex.ww's parsef64 uses:
|
||||
// build f64 constants via int-to-f64 casts.
|
||||
let f64tos_buf: [64]u8;
|
||||
|
||||
export fn f64tos(v: f64) str = {
|
||||
let out: i32 = 0;
|
||||
let f: f64 = v;
|
||||
let zero: f64 = 0: f64;
|
||||
if (f < zero) {
|
||||
f64tos_buf[out] = 45u8; // '-'
|
||||
out += 1;
|
||||
f = -f;
|
||||
};
|
||||
// 9e18 is comfortably under I64_MAX (9.22e18). Past this the
|
||||
// `f: i64` cast wraps and the integer part comes back as garbage.
|
||||
let cap: f64 = 9000000000000000000i64: f64;
|
||||
if (f >= cap) {
|
||||
let s: str = "huge";
|
||||
let k: i32 = 0;
|
||||
for (k < s.len) { f64tos_buf[out] = s[k]; out += 1; k += 1; };
|
||||
let r: str;
|
||||
r.ptr = &f64tos_buf[0];
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
let ip: i64 = f: i64;
|
||||
// Fractional part scaled to 6 decimal digits, with round-to-
|
||||
// nearest via +0.5. (f64 compound assigns mis-lower in cgen —
|
||||
// use the explicit form, as the rest of lib does.)
|
||||
let frac: f64 = f - (ip: f64);
|
||||
let scale: f64 = 1000000: f64;
|
||||
frac = frac * scale;
|
||||
let half: f64 = (1: f64) / (2: f64);
|
||||
let fp: i64 = (frac + half): i64;
|
||||
// Carry: e.g. 0.9999996 rounds fp up to 1000000 and the integer
|
||||
// part needs to advance.
|
||||
if (fp >= 1000000) {
|
||||
ip += 1;
|
||||
fp = 0;
|
||||
};
|
||||
let intstr: str = i64tos(ip, base.DEC);
|
||||
let k: i32 = 0;
|
||||
for (k < intstr.len) { f64tos_buf[out] = intstr.ptr[k]; out += 1; k += 1; };
|
||||
if (fp != 0) {
|
||||
f64tos_buf[out] = 46u8; // '.'
|
||||
out += 1;
|
||||
let fracstr: str = u64tos(fp: u64, base.DEC);
|
||||
// Pad fractional to 6 digits with leading zeros (e.g. 0.05 →
|
||||
// fp=50000, fracstr="50000", pad one '0' before).
|
||||
let z: i32 = 6 - fracstr.len;
|
||||
for (z > 0) { f64tos_buf[out] = 48u8; out += 1; z -= 1; };
|
||||
k = 0;
|
||||
for (k < fracstr.len) { f64tos_buf[out] = fracstr.ptr[k]; out += 1; k += 1; };
|
||||
// Trim trailing zeros in the fractional part.
|
||||
for (out > 0) {
|
||||
if (f64tos_buf[out - 1] != 48u8) { break; };
|
||||
out -= 1;
|
||||
};
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = &f64tos_buf[0];
|
||||
r.len = out;
|
||||
return r;
|
||||
};
|
||||
// f64tos — graduated to the Ryū shortest-round-trippable implementation
|
||||
// in ftos.ww (strconv #106 fold-5). The old lossy fixed-point version
|
||||
// (6 fractional digits, "huge" fallback ≥9e18, no NaN/Inf) was deleted
|
||||
// here per the lib-note graduation rule ("replace in one go, don't keep
|
||||
// both"); ftos.ww's f64tos is the live one. f32tos follows in fold-5b
|
||||
// (task #67, gated on the #143 f32-arg-push cgen fix).
|
||||
|
||||
// strerror — convert an strconv error to a user-readable string.
|
||||
// Returns owned str; release via os.free. Mirrors Hare's
|
||||
|
||||
Reference in New Issue
Block a user