diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-05-03 14:56:05 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-03 14:56:05 -0400 |
| commit | 6317da8da400e40a947f6333d1afc289ec349102 (patch) | |
| tree | 30e450f3c2277605b85cf07074505e52cfd86ae2 /lib/std/fmt/parse_float/parse_float.zig | |
| parent | d48789467d95ddf0582c855d01f32bef48726e29 (diff) | |
| parent | 2947a2faab2e287926b20d5d8984ad69bba5bd2c (diff) | |
| download | zig-6317da8da400e40a947f6333d1afc289ec349102.tar.gz zig-6317da8da400e40a947f6333d1afc289ec349102.zip | |
Merge pull request #11566 from tiehuis/master
add new float-parser based on eisel-lemire algorithm
Diffstat (limited to 'lib/std/fmt/parse_float/parse_float.zig')
| -rw-r--r-- | lib/std/fmt/parse_float/parse_float.zig | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/lib/std/fmt/parse_float/parse_float.zig b/lib/std/fmt/parse_float/parse_float.zig new file mode 100644 index 0000000000..d781b11495 --- /dev/null +++ b/lib/std/fmt/parse_float/parse_float.zig @@ -0,0 +1,64 @@ +const std = @import("std"); +const parse = @import("parse.zig"); +const parseNumber = parse.parseNumber; +const parseInfOrNan = parse.parseInfOrNan; +const convertFast = @import("convert_fast.zig").convertFast; +const convertEiselLemire = @import("convert_eisel_lemire.zig").convertEiselLemire; +const convertSlow = @import("convert_slow.zig").convertSlow; +const convertHex = @import("convert_hex.zig").convertHex; + +const optimize = true; + +pub const ParseFloatError = error{ + InvalidCharacter, +}; + +pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { + if (s.len == 0) { + return error.InvalidCharacter; + } + + var i: usize = 0; + const negative = s[i] == '-'; + if (s[i] == '-' or s[i] == '+') { + i += 1; + } + if (s.len == i) { + return error.InvalidCharacter; + } + + const n = parse.parseNumber(T, s[i..], negative) orelse { + return parse.parseInfOrNan(T, s[i..], negative) orelse error.InvalidCharacter; + }; + + if (n.hex) { + return convertHex(T, n); + } + + if (optimize) { + if (convertFast(T, n)) |f| { + return f; + } + + if (T == f16 or T == f32 or T == f64) { + // If significant digits were truncated, then we can have rounding error + // only if `mantissa + 1` produces a different result. We also avoid + // redundantly using the Eisel-Lemire algorithm if it was unable to + // correctly round on the first pass. + if (convertEiselLemire(T, n.exponent, n.mantissa)) |bf| { + if (!n.many_digits) { + return bf.toFloat(T, n.negative); + } + if (convertEiselLemire(T, n.exponent, n.mantissa + 1)) |bf2| { + if (bf.eql(bf2)) { + return bf.toFloat(T, n.negative); + } + } + } + } + } + + // Unable to correctly round the float using the Eisel-Lemire algorithm. + // Fallback to a slower, but always correct algorithm. + return convertSlow(T, s[i..]).toFloat(T, negative); +} |
