authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-02-15 17:32:13+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-02-15 17:32:13+13:00
log18ad50970f81bd4b07892a6651487be81effc4c7
treeb9905b0c64902191fcd7614e47f93b09ff74e4f8
parentde7c55145aeb6687040254dedaef66eedf5328dd

Make parseFloat stricter in what it accepts as input


2 files changed, 38 insertions(+), 43 deletions(-)

std/fmt/parse_float.zig+37-42
......@@ -84,10 +84,6 @@ const Z96 = struct {
8484 w += u64(d.d2) -% u64(s.d2);
8585 d.d2 = @truncate(u32, w);
8686 }
87
88 fn dump(d: Z96) void {
89 std.debug.warn("{} {} {}\n", d.d0, d.d1, d.d2);
90 }
9187};
9288
9389const FloatRepr = struct {
......@@ -178,7 +174,6 @@ fn convertRepr(comptime T: type, n: FloatRepr) T {
178174}
179175
180176const State = enum {
181 SkipLeadingWhitespace,
182177 MaybeSign,
183178 LeadingMantissaZeros,
184179 LeadingFractionalZeros,
......@@ -187,7 +182,6 @@ const State = enum {
187182 ExponentSign,
188183 LeadingExponentZeros,
189184 Exponent,
190 Stop,
191185};
192186
193187const ParseResult = enum {
......@@ -206,27 +200,19 @@ inline fn isSpace(c: u8) bool {
206200 return (c >= 0x09 and c <= 0x13) or c == 0x20;
207201}
208202
209fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {
203fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
210204 var digit_index: usize = 0;
211205 var negative = false;
212206 var negative_exp = false;
213207 var exponent: i32 = 0;
214208
215 var state = State.SkipLeadingWhitespace;
209 var state = State.MaybeSign;
216210
217211 var i: usize = 0;
218 loop: while (state != State.Stop and i < s.len) {
212 loop: while (i < s.len) {
219213 const c = s[i];
220214
221215 switch (state) {
222 State.SkipLeadingWhitespace => {
223 if (isSpace(c)) {
224 i += 1;
225 } else {
226 state = State.MaybeSign;
227 }
228 },
229
230216 State.MaybeSign => {
231217 state = State.LeadingMantissaZeros;
232218
......@@ -238,7 +224,7 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {
238224 } else if (isDigit(c) or c == '.') {
239225 // continue
240226 } else {
241 state = State.Stop;
227 return error.InvalidCharacter;
242228 }
243229 },
244230
......@@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {
329315
330316 i += 1;
331317 } else {
332 state = State.Stop;
318 return error.InvalidCharacter;
333319 }
334320 },
335
336 State.Stop => break :loop,
337321 }
338322 }
339323
......@@ -371,12 +355,10 @@ fn caseInEql(a: []const u8, b: []const u8) bool {
371355 return true;
372356}
373357
374pub fn parseFloat(comptime T: type, s: []const u8) T {
375 var r = FloatRepr{
376 .negative = false,
377 .exponent = 0,
378 .mantissa = 0,
379 };
358pub fn parseFloat(comptime T: type, s: []const u8) !T {
359 if (s.len == 0) {
360 return error.InvalidCharacter;
361 }
380362
381363 if (caseInEql(s, "nan")) {
382364 return std.math.nan(T);
......@@ -386,7 +368,13 @@ pub fn parseFloat(comptime T: type, s: []const u8) T {
386368 return -std.math.inf(T);
387369 }
388370
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)) {
390378 ParseResult.Ok => convertRepr(T, r),
391379 ParseResult.PlusZero => 0.0,
392380 ParseResult.MinusZero => -T(0.0),
......@@ -396,30 +384,37 @@ pub fn parseFloat(comptime T: type, s: []const u8) T {
396384}
397385
398386test "fmt.parseFloat" {
399 const assert = std.debug.assert;
387 const testing = std.testing;
388 const expect = testing.expect;
389 const expectEqual = testing.expectEqual;
400390 const approxEq = std.math.approxEq;
401391 const epsilon = 1e-7;
402392
403 inline for ([]type{ f32, f64, f128 }) |T| {
393 inline for ([]type{ f16, f32, f64, f128 }) |T| {
404394 const Z = @IntType(false, T.bit_count);
405395
406 assert(parseFloat(T, "0") == 0.0);
407 assert(parseFloat(T, "+0") == 0.0);
408 assert(parseFloat(T, "-0") == 0.0);
396 testing.expectError(error.InvalidCharacter, parseFloat(T, ""));
397 testing.expectError(error.InvalidCharacter, parseFloat(T, " 1"));
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);
409404
410 assert(approxEq(T, parseFloat(T, "3.141"), 3.141, epsilon));
411 assert(approxEq(T, parseFloat(T, "-3.141"), -3.141, epsilon));
405 expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
406 expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
412407
413 assert(parseFloat(T, "1e-700") == 0);
414 assert(parseFloat(T, "1e+700") == std.math.inf(T));
408 expectEqual((try parseFloat(T, "1e-700")), 0);
409 expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T));
415410
416 assert(@bitCast(Z, parseFloat(T, "nAn")) == @bitCast(Z, std.math.nan(T)));
417 assert(parseFloat(T, "inF") == std.math.inf(T));
418 assert(parseFloat(T, "-INF") == -std.math.inf(T));
411 expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T)));
412 expectEqual((try parseFloat(T, "inF")), std.math.inf(T));
413 expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T));
419414
420415 if (T != f16) {
421 assert(approxEq(T, parseFloat(T, "123142.1"), 123142.1, epsilon));
422 assert(approxEq(T, parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon));
416 expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon));
417 expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon));
423418 }
424419 }
425420}
std/json.zig+1-1
......@@ -1345,7 +1345,7 @@ pub const Parser = struct {
13451345 return if (token.number_is_integer)
13461346 Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
13471347 else
1348 Value{ .Float = std.fmt.parseFloat(f64, token.slice(input, i)) };
1348 Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) };
13491349 }
13501350};
13511351