-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
std.fmt: add ryu floating-point formatting implementation #19229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
binaryToDecimal
function 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