diff options
| -rw-r--r-- | lib/std/fmt/parse_float.zig | 72 | ||||
| -rw-r--r-- | lib/std/fmt/parse_float/parse_float.zig | 66 |
2 files changed, 67 insertions, 71 deletions
diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 2f75e83966..854108db5e 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -1,7 +1,4 @@ -pub const parseFloat = @import("parse_float/parse_float.zig").parseFloat; -pub const ParseFloatError = @import("parse_float/parse_float.zig").ParseFloatError; - -const std = @import("std"); +const std = @import("../std.zig"); const math = std.math; const testing = std.testing; const expect = testing.expect; @@ -9,10 +6,75 @@ const expectEqual = testing.expectEqual; const expectError = testing.expectError; const approxEqAbs = std.math.approxEqAbs; const epsilon = 1e-7; +const parse = @import("parse_float/parse.zig"); +const convertHex = @import("parse_float/convert_hex.zig").convertHex; +const convertFast = @import("parse_float/convert_fast.zig").convertFast; +const convertEiselLemire = @import("parse_float/convert_eisel_lemire.zig").convertEiselLemire; +const convertSlow = @import("parse_float/convert_slow.zig").convertSlow; + +pub const ParseFloatError = error{ + InvalidCharacter, +}; + +pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { + if (@typeInfo(T) != .Float) { + @compileError("Cannot parse a float into a non-floating point type."); + } + + if (T == f80) { + @compileError("TODO support parsing float to f80"); + } + + 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 (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); +} // See https://github.com/tiehuis/parse-number-fxx-test-data for a wider-selection of test-data. -test "parseFloat" { +test parseFloat { inline for ([_]type{ f16, f32, f64, f128 }) |T| { try testing.expectError(error.InvalidCharacter, parseFloat(T, "")); try testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); diff --git a/lib/std/fmt/parse_float/parse_float.zig b/lib/std/fmt/parse_float/parse_float.zig deleted file mode 100644 index d7980d8937..0000000000 --- a/lib/std/fmt/parse_float/parse_float.zig +++ /dev/null @@ -1,66 +0,0 @@ -const std = @import("std"); -const parse = @import("parse.zig"); -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; - -pub const ParseFloatError = error{ - InvalidCharacter, -}; - -pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { - if (@typeInfo(T) != .Float) { - @compileError("Cannot parse a float into a non-floating point type."); - } - - if (T == f80) { - @compileError("TODO support parsing float to f80"); - } - - 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 (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); -} |
