aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fmt/parse_float/parse_float.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-05-03 14:56:05 -0400
committerGitHub <noreply@github.com>2022-05-03 14:56:05 -0400
commit6317da8da400e40a947f6333d1afc289ec349102 (patch)
tree30e450f3c2277605b85cf07074505e52cfd86ae2 /lib/std/fmt/parse_float/parse_float.zig
parentd48789467d95ddf0582c855d01f32bef48726e29 (diff)
parent2947a2faab2e287926b20d5d8984ad69bba5bd2c (diff)
downloadzig-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.zig64
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);
+}