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 {...@@ -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};
9288
93const FloatRepr = struct {89const 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}
179175
180const State = enum {176const 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};
192186
193const ParseResult = enum {187const 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}
208202
209fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {203fn 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;
214208
215 var state = State.SkipLeadingWhitespace;209 var state = State.MaybeSign;
216210
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];
220214
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;
232218
...@@ -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 // continue225 // continue
240 } else {226 } else {
241 state = State.Stop;227 return error.InvalidCharacter;
242 }228 }
243 },229 },
244230
...@@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {...@@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult {
329315
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 }
339323
...@@ -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}
373357
374pub fn parseFloat(comptime T: type, s: []const u8) T {358pub 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 };
380362
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 }
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)) {
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}
397385
398test "fmt.parseFloat" {386test "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;
402392
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);
405395
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);
409404
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));
412407
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));
415410
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));
419414
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}
std/json.zig+1-1
...@@ -1345,7 +1345,7 @@ pub const Parser = struct {...@@ -1345,7 +1345,7 @@ pub const Parser = struct {
1345 return if (token.number_is_integer)1345 return if (token.number_is_integer)
1346 Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }1346 Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
1347 else1347 else
1348 Value{ .Float = std.fmt.parseFloat(f64, token.slice(input, i)) };1348 Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) };
1349 }1349 }
1350};1350};
13511351