| author | |
| committer | |
| log | b4bf3bdf7eac05c5e4ff887294385946f4dd5f3f |
| tree | 714937345eadfd8ccb5332ae87179083260e4029 |
| parent | 364e53f3bf6b5aa4e5e7eba5d790c5b957007067 |
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 #112832 files changed, 8 insertions(+), 1 deletions(-)
lib/std/fmt.zig+2| ... | @@ -2299,6 +2299,8 @@ test "float.decimal" { | ... | @@ -2299,6 +2299,8 @@ test "float.decimal" { |
| 2299 | try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); | 2299 | try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); |
| 2300 | try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); | 2300 | try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); |
| 2301 | try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)}); | 2301 | try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)}); |
| 2302 | try expectFmt("f64: 10000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e37)}); | ||
| 2303 | try expectFmt("f64: 100000000000000000000000000000000000000", "f64: {d}", .{@as(f64, 1e38)}); | ||
| 2302 | } | 2304 | } |
| 2303 | 2305 | ||
| 2304 | test "float.libc.sanity" { | 2306 | test "float.libc.sanity" { |
lib/std/fmt/errol.zig+6-1| ... | @@ -106,7 +106,10 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { | ... | @@ -106,7 +106,10 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { |
| 106 | } else if (val >= 16.0 and val < 9.007199254740992e15) { | 106 | } else if (val >= 16.0 and val < 9.007199254740992e15) { |
| 107 | return errolFixed(val, buffer); | 107 | return errolFixed(val, buffer); |
| 108 | } | 108 | } |
| 109 | return errolSlow(val, buffer); | ||
| 110 | } | ||
| 109 | 111 | ||
| 112 | fn errolSlow(val: f64, buffer: []u8) FloatDecimal { | ||
| 110 | // normalize the midpoint | 113 | // normalize the midpoint |
| 111 | 114 | ||
| 112 | const e = math.frexp(val).exponent; | 115 | const e = math.frexp(val).exponent; |
| ... | @@ -336,7 +339,9 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { | ... | @@ -336,7 +339,9 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { |
| 336 | var buf_index = u64toa(m64, buffer) - 1; | 339 | var buf_index = u64toa(m64, buffer) - 1; |
| 337 | 340 | ||
| 338 | if (mi != 0) { | 341 | if (mi != 0) { |
| 339 | buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); | 342 | const round_up = buffer[buf_index] >= '5'; |
| 343 | if (buf_index == 0 or (round_up and buffer[buf_index - 1] == '9')) return errolSlow(val, buffer); | ||
| 344 | buffer[buf_index - 1] += @boolToInt(round_up); | ||
| 340 | } else { | 345 | } else { |
| 341 | buf_index += 1; | 346 | buf_index += 1; |
| 342 | } | 347 | } |