From b4bf3bdf7eac05c5e4ff887294385946f4dd5f3f Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 3 Apr 2022 08:47:17 -0700 Subject: [PATCH] std.fmt: Fix incorrect behavior with large floating point integers. I consider this an interim workaround/hack until #1299 is finished. There is a bug in the original C implementation of the errol3 (and errol4) algorithm that can result in undefined behavior or an obviously incorrect result (leading ':' in the output) This change checks for those two problems and uses a slower fallback path if they occur. I can't guarantee that this will always produce the correct result, but since the workaround is only used if the original algorithm is guaranteed to fail, it should never turn a previously-correct result into an incorrect one. Fixes #11283 --- lib/std/fmt.zig | 2 ++ lib/std/fmt/errol.zig | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 395c502d61..6d4f3a1daa 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2299,6 +2299,8 @@ test "float.decimal" { try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)}); + try expectFmt("f64: 10000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e37)}); + try expectFmt("f64: 100000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e38)}); } test "float.libc.sanity" { diff --git a/lib/std/fmt/errol.zig b/lib/std/fmt/errol.zig index e98c23f6ec..29dd2b7a63 100644 --- a/lib/std/fmt/errol.zig +++ b/lib/std/fmt/errol.zig @@ -106,7 +106,10 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { } else if (val >= 16.0 and val < 9.007199254740992e15) { return errolFixed(val, buffer); } + return errolSlow(val, buffer); +} +fn errolSlow(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; @@ -336,7 +339,9 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); + const round_up = buffer[buf_index] >= '5'; + if (buf_index == 0 or (round_up and buffer[buf_index - 1] == '9')) return errolSlow(val, buffer); + buffer[buf_index - 1] += @boolToInt(round_up); } else { buf_index += 1; }