| ... | @@ -84,10 +84,6 @@ const Z96 = struct { | ... | @@ -84,10 +84,6 @@ const Z96 = struct { |
| 84 | w += u64(d.d2) -% u64(s.d2); | 84 | w += u64(d.d2) -% u64(s.d2); |
| 85 | d.d2 = @truncate(u32, w); | 85 | d.d2 = @truncate(u32, w); |
| 86 | } | 86 | } |
| 87 | | | |
| 88 | fn dump(d: Z96) void { | | |
| 89 | std.debug.warn("{} {} {}\n", d.d0, d.d1, d.d2); | | |
| 90 | } | | |
| 91 | }; | 87 | }; |
| 92 | | 88 | |
| 93 | const FloatRepr = struct { | 89 | const FloatRepr = struct { |
| ... | @@ -178,7 +174,6 @@ fn convertRepr(comptime T: type, n: FloatRepr) T { | ... | @@ -178,7 +174,6 @@ fn convertRepr(comptime T: type, n: FloatRepr) T { |
| 178 | } | 174 | } |
| 179 | | 175 | |
| 180 | const State = enum { | 176 | const State = enum { |
| 181 | SkipLeadingWhitespace, | | |
| 182 | MaybeSign, | 177 | MaybeSign, |
| 183 | LeadingMantissaZeros, | 178 | LeadingMantissaZeros, |
| 184 | LeadingFractionalZeros, | 179 | LeadingFractionalZeros, |
| ... | @@ -187,7 +182,6 @@ const State = enum { | ... | @@ -187,7 +182,6 @@ const State = enum { |
| 187 | ExponentSign, | 182 | ExponentSign, |
| 188 | LeadingExponentZeros, | 183 | LeadingExponentZeros, |
| 189 | Exponent, | 184 | Exponent, |
| 190 | Stop, | | |
| 191 | }; | 185 | }; |
| 192 | | 186 | |
| 193 | const ParseResult = enum { | 187 | const ParseResult = enum { |
| ... | @@ -206,27 +200,19 @@ inline fn isSpace(c: u8) bool { | ... | @@ -206,27 +200,19 @@ inline fn isSpace(c: u8) bool { |
| 206 | return (c >= 0x09 and c <= 0x13) or c == 0x20; | 200 | return (c >= 0x09 and c <= 0x13) or c == 0x20; |
| 207 | } | 201 | } |
| 208 | | 202 | |
| 209 | fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { | 203 | fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { |
| 210 | var digit_index: usize = 0; | 204 | var digit_index: usize = 0; |
| 211 | var negative = false; | 205 | var negative = false; |
| 212 | var negative_exp = false; | 206 | var negative_exp = false; |
| 213 | var exponent: i32 = 0; | 207 | var exponent: i32 = 0; |
| 214 | | 208 | |
| 215 | var state = State.SkipLeadingWhitespace; | 209 | var state = State.MaybeSign; |
| 216 | | 210 | |
| 217 | var i: usize = 0; | 211 | var i: usize = 0; |
| 218 | loop: while (state != State.Stop and i < s.len) { | 212 | loop: while (i < s.len) { |
| 219 | const c = s[i]; | 213 | const c = s[i]; |
| 220 | | 214 | |
| 221 | switch (state) { | 215 | switch (state) { |
| 222 | State.SkipLeadingWhitespace => { | | |
| 223 | if (isSpace(c)) { | | |
| 224 | i += 1; | | |
| 225 | } else { | | |
| 226 | state = State.MaybeSign; | | |
| 227 | } | | |
| 228 | }, | | |
| 229 | | | |
| 230 | State.MaybeSign => { | 216 | State.MaybeSign => { |
| 231 | state = State.LeadingMantissaZeros; | 217 | state = State.LeadingMantissaZeros; |
| 232 | | 218 | |
| ... | @@ -238,7 +224,7 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { | ... | @@ -238,7 +224,7 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { |
| 238 | } else if (isDigit(c) or c == '.') { | 224 | } else if (isDigit(c) or c == '.') { |
| 239 | // continue | 225 | // continue |
| 240 | } else { | 226 | } else { |
| 241 | state = State.Stop; | 227 | return error.InvalidCharacter; |
| 242 | } | 228 | } |
| 243 | }, | 229 | }, |
| 244 | | 230 | |
| ... | @@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { | ... | @@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { |
| 329 | | 315 | |
| 330 | i += 1; | 316 | i += 1; |
| 331 | } else { | 317 | } else { |
| 332 | state = State.Stop; | 318 | return error.InvalidCharacter; |
| 333 | } | 319 | } |
| 334 | }, | 320 | }, |
| 335 | | | |
| 336 | State.Stop => break :loop, | | |
| 337 | } | 321 | } |
| 338 | } | 322 | } |
| 339 | | 323 | |
| ... | @@ -371,12 +355,10 @@ fn caseInEql(a: []const u8, b: []const u8) bool { | ... | @@ -371,12 +355,10 @@ fn caseInEql(a: []const u8, b: []const u8) bool { |
| 371 | return true; | 355 | return true; |
| 372 | } | 356 | } |
| 373 | | 357 | |
| 374 | pub fn parseFloat(comptime T: type, s: []const u8) T { | 358 | pub fn parseFloat(comptime T: type, s: []const u8) !T { |
| 375 | var r = FloatRepr{ | 359 | if (s.len == 0) { |
| 376 | .negative = false, | 360 | return error.InvalidCharacter; |
| 377 | .exponent = 0, | 361 | } |
| 378 | .mantissa = 0, | | |
| 379 | }; | | |
| 380 | | 362 | |
| 381 | if (caseInEql(s, "nan")) { | 363 | if (caseInEql(s, "nan")) { |
| 382 | return std.math.nan(T); | 364 | return std.math.nan(T); |
| ... | @@ -386,7 +368,13 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { | ... | @@ -386,7 +368,13 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { |
| 386 | return -std.math.inf(T); | 368 | return -std.math.inf(T); |
| 387 | } | 369 | } |
| 388 | | 370 | |
| 389 | return switch (parseRepr(s, &r)) { | 371 | var r = FloatRepr{ |
| | 372 | .negative = false, |
| | 373 | .exponent = 0, |
| | 374 | .mantissa = 0, |
| | 375 | }; |
| | 376 | |
| | 377 | return switch (try parseRepr(s, &r)) { |
| 390 | ParseResult.Ok => convertRepr(T, r), | 378 | ParseResult.Ok => convertRepr(T, r), |
| 391 | ParseResult.PlusZero => 0.0, | 379 | ParseResult.PlusZero => 0.0, |
| 392 | ParseResult.MinusZero => -T(0.0), | 380 | ParseResult.MinusZero => -T(0.0), |
| ... | @@ -396,30 +384,37 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { | ... | @@ -396,30 +384,37 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { |
| 396 | } | 384 | } |
| 397 | | 385 | |
| 398 | test "fmt.parseFloat" { | 386 | test "fmt.parseFloat" { |
| 399 | const assert = std.debug.assert; | 387 | const testing = std.testing; |
| | 388 | const expect = testing.expect; |
| | 389 | const expectEqual = testing.expectEqual; |
| 400 | const approxEq = std.math.approxEq; | 390 | const approxEq = std.math.approxEq; |
| 401 | const epsilon = 1e-7; | 391 | const epsilon = 1e-7; |
| 402 | | 392 | |
| 403 | inline for ([]type{ f32, f64, f128 }) |T| { | 393 | inline for ([]type{ f16, f32, f64, f128 }) |T| { |
| 404 | const Z = @IntType(false, T.bit_count); | 394 | const Z = @IntType(false, T.bit_count); |
| 405 | | 395 | |
| 406 | assert(parseFloat(T, "0") == 0.0); | 396 | testing.expectError(error.InvalidCharacter, parseFloat(T, "")); |
| 407 | assert(parseFloat(T, "+0") == 0.0); | 397 | testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); |
| 408 | assert(parseFloat(T, "-0") == 0.0); | 398 | testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); |
| | 399 | |
| | 400 | expectEqual(try parseFloat(T, "0"), 0.0); |
| | 401 | expectEqual((try parseFloat(T, "0")), 0.0); |
| | 402 | expectEqual((try parseFloat(T, "+0")), 0.0); |
| | 403 | expectEqual((try parseFloat(T, "-0")), 0.0); |
| 409 | | 404 | |
| 410 | assert(approxEq(T, parseFloat(T, "3.141"), 3.141, epsilon)); | 405 | expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon)); |
| 411 | assert(approxEq(T, parseFloat(T, "-3.141"), -3.141, epsilon)); | 406 | expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); |
| 412 | | 407 | |
| 413 | assert(parseFloat(T, "1e-700") == 0); | 408 | expectEqual((try parseFloat(T, "1e-700")), 0); |
| 414 | assert(parseFloat(T, "1e+700") == std.math.inf(T)); | 409 | expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T)); |
| 415 | | 410 | |
| 416 | assert(@bitCast(Z, parseFloat(T, "nAn")) == @bitCast(Z, std.math.nan(T))); | 411 | expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); |
| 417 | assert(parseFloat(T, "inF") == std.math.inf(T)); | 412 | expectEqual((try parseFloat(T, "inF")), std.math.inf(T)); |
| 418 | assert(parseFloat(T, "-INF") == -std.math.inf(T)); | 413 | expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T)); |
| 419 | | 414 | |
| 420 | if (T != f16) { | 415 | if (T != f16) { |
| 421 | assert(approxEq(T, parseFloat(T, "123142.1"), 123142.1, epsilon)); | 416 | expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); |
| 422 | assert(approxEq(T, parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); | 417 | expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); |
| 423 | } | 418 | } |
| 424 | } | 419 | } |
| 425 | } | 420 | } |