lib/fmt: render i64::MIN via math.absi64, not in-i64 negate (#67)

i64dec computed the magnitude with `n = -n` inside i64, which wraps at
i64::MIN (-MIN == MIN stays negative), so the `for (n > 0)` loop never
ran and println(i64::MIN) emitted only the bare '-'. The whole print/
println/fprint family was affected at the single boundary value (and
int MIN, since int is 8B). ref/hare/fmt/print.ha:124-129 takes the
magnitude through math::absi64 into a u64; do the same (math.absi64
exists, lib/math/math.ww:12). ww's own printf path was already correct
((-v): u64), so print and printf now agree.

fmttest gains fprinti64min + fprintintmin pinning the boundary.
This commit is contained in:
2026-06-13 10:33:57 +09:00
parent 5cab22ecec
commit 69e31355ce
2 changed files with 49 additions and 8 deletions

View File

@@ -22,6 +22,7 @@
package fmt;
import io;
import math;
import memio;
import os;
import strings;
@@ -38,16 +39,18 @@ let i64dec_buf: [21]u8;
// of strconv.i64tos for the pre-printf-family print path; the
// printf-family path below dispatches through strconv directly.
fn i64dec(v: i64) str = {
let neg: bool = false;
let n: i64 = v;
if (n < 0) { neg = true; n = -n; };
// ref/hare/fmt/print.ha:124-129: magnitude via math::absi64 into u64,
// sign tested separately. `n = -n` on an i64 wraps at i64::MIN
// (-MIN == MIN), so the old loop never ran and only the '-' printed.
let neg: bool = v < 0;
let n: u64 = math.absi64(v);
let tmp: [20]u8;
let i: i32 = 0;
if (n == 0) { tmp[0] = '0'; i = 1; };
for (n > 0) {
let d: i64 = n % 10i64;
tmp[i] = (d + 48i64): u8;
n = n / 10i64;
if (n == 0u64) { tmp[0] = '0'; i = 1; };
for (n > 0u64) {
let d: u64 = n % 10u64;
tmp[i] = (d + 48u64): u8;
n = n / 10u64;
i += 1;
};
let out: i32 = 0;

View File

@@ -76,6 +76,44 @@ fn errsource() io.stream = {
match (c) { case void => {}; case let eioe: io.error => abort(); };
};
// ---- fprint: i64::MIN / int MIN whole-magnitude (#67) ------------------
// i64dec used `n = -n` within i64, which wraps at i64::MIN (-MIN == MIN),
// so the digit loop never ran and only the bare '-' was emitted. The fix
// takes the magnitude through math.absi64 into u64. These rows pin the
// boundary value plus a normal negative, zero, and int MIN.
@test fn fprinti64min() void = {
let mem: memio.stream = memio.dynamic();
let s: io.stream = &mem.vt;
let m: i64 = -9223372036854775807i64 - 1i64;
let r: (size | io.error) = fmt.fprint(s, m);
match (r) {
case let n: size => { assert(!(n: i32 != 20)); };
case let eioe: io.error => abort();
};
assert(!(!streq(memio.string(&mem), "-9223372036854775808")));
let c: (void | io.error) = io.close(s);
match (c) { case void => {}; case let eioe: io.error => abort(); };
};
@test fn fprintintmin() void = {
let mem: memio.stream = memio.dynamic();
let s: io.stream = &mem.vt;
let m: int = -9223372036854775807 - 1;
let r: (size | io.error) = fmt.fprint(s, m, -7i64, 0i64);
match (r) {
case let n: size => { assert(!(n: i32 != 25)); }; // "-9223372036854775808 -7 0"
case let eioe: io.error => abort();
};
assert(!(!streq(memio.string(&mem), "-9223372036854775808 -7 0")));
let c: (void | io.error) = io.close(s);
match (c) { case void => {}; case let eioe: io.error => abort(); };
};
// ---- fprint: bool + rune renders as "true A" --------------------------
@test fn fprintboolrune() void = {