std.fmt: add ryu floating-point formatting implementation#19229
std.fmt: add ryu floating-point formatting implementation#19229andrewrk merged 6 commits intoziglang:masterfrom
Conversation
This replaces the errol backend with one based on ryu. The 128-bit backend only is implemented. This supports all floating-point types and does not use fp logic to print. Closes ziglang#1181. Closes ziglang#1299. Closes ziglang#3612.
| formatFloatScientific(value, options, buf_stream.writer()) catch |err| switch (err) { | ||
| error.NoSpaceLeft => unreachable, | ||
| const s = ryu128.format(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { | ||
| error.BufferTooSmall => "(float)", |
There was a problem hiding this comment.
I am not a big fan of this. Previously the 512-byte buffer was sufficient for f64. f80 and f128 need a lot more space (~5k bytes) which is impractical to increase too imo.
| /// value can be re-parsed back to the same type unambiguously. | ||
| /// | ||
| /// Floats with more than 64 are currently rounded, see https://github.com/ziglang/zig/issues/1181 | ||
| pub fn formatFloatScientific( |
There was a problem hiding this comment.
These could use a compileError to deprecate. Alternatively, we could provide this as a slim wrapper over the actual ryu implementations (which are not currently exposed).
| pub const min_buffer_size = 53; | ||
|
|
||
| /// Returns the minimum buffer size needed to print every float of a specific type and format. | ||
| pub fn bufferSize(comptime mode: Format, comptime T: type) comptime_int { |
There was a problem hiding this comment.
This needs to be public if we actually want external users to be able take advantage of this. I originally preferred the direct buffer write approach instead of using a fmt writer.
Typically though, a user can get away with much less and these are only a concern for unbounded full-precision decimal output.
|
I'm leaning towards renaming |
|
Awesome. |
std.fmt: add ryu floating-point formatting implementation
This PR replaces the existing errol floating point formatting algorithm with one based on Ryu.
Ryu is an algorithm for converting IEEE-754 floating-point numbers to decimal strings: https://github.com/ulfjack/ryu
The improvements this PR brings are:
See https://github.com/tiehuis/zig-ryu/tree/05927ac704170fe6c98994eb1281ad6f42034e20/src/ryu128 for accompanying programs (fuzz/tests) presented in this PR.
The
binaryToDecimalfunction is the only thing ported from ryu. The 128-bit backend upstream does not provide a fixed-precision formatting mode. I have implemented this from scratch along with rounding (loosely adapted from the existing errol round mechanism).Closes #1181.
Closes #1299.
Closes #3612.
Behaviour Differences
Additionally, rounding behaviour in these cases can differ in the fixed precision case as the shortest representation will typically differ.
Performance
See https://github.com/tiehuis/zig-ryu/blob/05927ac704170fe6c98994eb1281ad6f42034e20/src/ryu128/perf.zig for the program used to test performance. The ryu implementation is not optimized in depth.
Errol
Ryu
We see ~2.3x performance improvement.
Code Size
See:
Errol
Ryu
We do see a moderate increase in code size. The errol code size above also is likely slightly underreported due to not all showing under the same namespace.
Note too that we are receiving a lot more functionality with the new implementation as well. The old is incorrect in many cases. The 128-bit backend as well is identical between different floating point types so will not increase if using between many different types.
Testing
I have tested the following:
Future Work