| ... | @@ -1,7 +1,4 @@ | ... | @@ -1,7 +1,4 @@ |
| 1 | pub const parseFloat = @import("parse_float/parse_float.zig").parseFloat; | 1 | const std = @import("../std.zig"); |
| 2 | pub const ParseFloatError = @import("parse_float/parse_float.zig").ParseFloatError; | | |
| 3 | | | |
| 4 | const std = @import("std"); | | |
| 5 | const math = std.math; | 2 | const math = std.math; |
| 6 | const testing = std.testing; | 3 | const testing = std.testing; |
| 7 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| ... | @@ -9,10 +6,75 @@ const expectEqual = testing.expectEqual; | ... | @@ -9,10 +6,75 @@ const expectEqual = testing.expectEqual; |
| 9 | const expectError = testing.expectError; | 6 | const expectError = testing.expectError; |
| 10 | const approxEqAbs = std.math.approxEqAbs; | 7 | const approxEqAbs = std.math.approxEqAbs; |
| 11 | const epsilon = 1e-7; | 8 | const epsilon = 1e-7; |
| | 9 | const parse = @import("parse_float/parse.zig"); |
| | 10 | const convertHex = @import("parse_float/convert_hex.zig").convertHex; |
| | 11 | const convertFast = @import("parse_float/convert_fast.zig").convertFast; |
| | 12 | const convertEiselLemire = @import("parse_float/convert_eisel_lemire.zig").convertEiselLemire; |
| | 13 | const convertSlow = @import("parse_float/convert_slow.zig").convertSlow; |
| | 14 | |
| | 15 | pub const ParseFloatError = error{ |
| | 16 | InvalidCharacter, |
| | 17 | }; |
| | 18 | |
| | 19 | pub fn parseFloat(comptime T: type, s: []const u8) ParseFloatError!T { |
| | 20 | if (@typeInfo(T) != .Float) { |
| | 21 | @compileError("Cannot parse a float into a non-floating point type."); |
| | 22 | } |
| | 23 | |
| | 24 | if (T == f80) { |
| | 25 | @compileError("TODO support parsing float to f80"); |
| | 26 | } |
| | 27 | |
| | 28 | if (s.len == 0) { |
| | 29 | return error.InvalidCharacter; |
| | 30 | } |
| | 31 | |
| | 32 | var i: usize = 0; |
| | 33 | const negative = s[i] == '-'; |
| | 34 | if (s[i] == '-' or s[i] == '+') { |
| | 35 | i += 1; |
| | 36 | } |
| | 37 | if (s.len == i) { |
| | 38 | return error.InvalidCharacter; |
| | 39 | } |
| | 40 | |
| | 41 | const n = parse.parseNumber(T, s[i..], negative) orelse { |
| | 42 | return parse.parseInfOrNan(T, s[i..], negative) orelse error.InvalidCharacter; |
| | 43 | }; |
| | 44 | |
| | 45 | if (n.hex) { |
| | 46 | return convertHex(T, n); |
| | 47 | } |
| | 48 | |
| | 49 | if (convertFast(T, n)) |f| { |
| | 50 | return f; |
| | 51 | } |
| | 52 | |
| | 53 | if (T == f16 or T == f32 or T == f64) { |
| | 54 | // If significant digits were truncated, then we can have rounding error |
| | 55 | // only if `mantissa + 1` produces a different result. We also avoid |
| | 56 | // redundantly using the Eisel-Lemire algorithm if it was unable to |
| | 57 | // correctly round on the first pass. |
| | 58 | if (convertEiselLemire(T, n.exponent, n.mantissa)) |bf| { |
| | 59 | if (!n.many_digits) { |
| | 60 | return bf.toFloat(T, n.negative); |
| | 61 | } |
| | 62 | if (convertEiselLemire(T, n.exponent, n.mantissa + 1)) |bf2| { |
| | 63 | if (bf.eql(bf2)) { |
| | 64 | return bf.toFloat(T, n.negative); |
| | 65 | } |
| | 66 | } |
| | 67 | } |
| | 68 | } |
| | 69 | |
| | 70 | // Unable to correctly round the float using the Eisel-Lemire algorithm. |
| | 71 | // Fallback to a slower, but always correct algorithm. |
| | 72 | return convertSlow(T, s[i..]).toFloat(T, negative); |
| | 73 | } |
| 12 | | 74 | |
| 13 | // See https://github.com/tiehuis/parse-number-fxx-test-data for a wider-selection of test-data. | 75 | // See https://github.com/tiehuis/parse-number-fxx-test-data for a wider-selection of test-data. |
| 14 | | 76 | |
| 15 | test "parseFloat" { | 77 | test parseFloat { |
| 16 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | 78 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { |
| 17 | try testing.expectError(error.InvalidCharacter, parseFloat(T, "")); | 79 | try testing.expectError(error.InvalidCharacter, parseFloat(T, "")); |
| 18 | try testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); | 80 | try testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); |