Skip to content

Commit

Permalink
std.fmt: Fix incorrect behavior with large floating point integers.
Browse files Browse the repository at this point in the history
I consider this an interim workaround/hack until ziglang#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 ziglang#11283
  • Loading branch information
ehaas committed Apr 3, 2022
1 parent 91eb1af commit e1ba450
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/std/fmt.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
7 changes: 6 additions & 1 deletion lib/std/fmt/errol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit e1ba450

Please sign in to comment.