authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-29 22:40:32-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-03-29 22:40:32-04:00
logb717df786bbf9b42abf1db1ec3fdae183debfdea
tree84320af40ef7ee890c1896aa51ec3319f9fe9c00
parentf9f7deaeda20b212ae4daea0009a904beccdf828
parent6809222d32e204ac46ee7b7b86a6ba5ebce955e6
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4845 from xackus/fix-parseFloat

fix overflow in parseFloat and cleanup

2 files changed, 54 insertions(+), 76 deletions(-)

lib/std/fmt/parse_float.zig+51-71
......@@ -30,6 +30,7 @@
3030// - Does not handle denormals
3131
3232const std = @import("../std.zig");
33const ascii = std.ascii;
3334
3435const max_digits = 25;
3536
......@@ -190,14 +191,6 @@ const ParseResult = enum {
190191 MinusInf,
191192};
192193
193inline fn isDigit(c: u8) bool {
194 return c >= '0' and c <= '9';
195}
196
197inline fn isSpace(c: u8) bool {
198 return (c >= 0x09 and c <= 0x13) or c == 0x20;
199}
200
201194fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
202195 var digit_index: usize = 0;
203196 var negative = false;
......@@ -207,52 +200,49 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
207200 var state = State.MaybeSign;
208201
209202 var i: usize = 0;
210 loop: while (i < s.len) {
203 while (i < s.len) {
211204 const c = s[i];
212205
213206 switch (state) {
214 State.MaybeSign => {
215 state = State.LeadingMantissaZeros;
207 .MaybeSign => {
208 state = .LeadingMantissaZeros;
216209
217210 if (c == '+') {
218211 i += 1;
219212 } else if (c == '-') {
220213 n.negative = true;
221214 i += 1;
222 } else if (isDigit(c) or c == '.') {
215 } else if (ascii.isDigit(c) or c == '.') {
223216 // continue
224217 } else {
225218 return error.InvalidCharacter;
226219 }
227220 },
228
229 State.LeadingMantissaZeros => {
221 .LeadingMantissaZeros => {
230222 if (c == '0') {
231223 i += 1;
232224 } else if (c == '.') {
233225 i += 1;
234 state = State.LeadingFractionalZeros;
226 state = .LeadingFractionalZeros;
235227 } else {
236 state = State.MantissaIntegral;
228 state = .MantissaIntegral;
237229 }
238230 },
239
240 State.LeadingFractionalZeros => {
231 .LeadingFractionalZeros => {
241232 if (c == '0') {
242233 i += 1;
243234 if (n.exponent > std.math.minInt(i32)) {
244235 n.exponent -= 1;
245236 }
246237 } else {
247 state = State.MantissaFractional;
238 state = .MantissaFractional;
248239 }
249240 },
250
251 State.MantissaIntegral => {
252 if (isDigit(c)) {
241 .MantissaIntegral => {
242 if (ascii.isDigit(c)) {
253243 if (digit_index < max_digits) {
254244 n.mantissa *%= 10;
255 n.mantissa += s[i] - '0';
245 n.mantissa += c - '0';
256246 digit_index += 1;
257247 } else if (n.exponent < std.math.maxInt(i32)) {
258248 n.exponent += 1;
......@@ -261,14 +251,13 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
261251 i += 1;
262252 } else if (c == '.') {
263253 i += 1;
264 state = State.MantissaFractional;
254 state = .MantissaFractional;
265255 } else {
266 state = State.MantissaFractional;
256 state = .MantissaFractional;
267257 }
268258 },
269
270 State.MantissaFractional => {
271 if (isDigit(c)) {
259 .MantissaFractional => {
260 if (ascii.isDigit(c)) {
272261 if (digit_index < max_digits) {
273262 n.mantissa *%= 10;
274263 n.mantissa += c - '0';
......@@ -279,13 +268,12 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
279268 i += 1;
280269 } else if (c == 'e' or c == 'E') {
281270 i += 1;
282 state = State.ExponentSign;
271 state = .ExponentSign;
283272 } else {
284 state = State.ExponentSign;
273 state = .ExponentSign;
285274 }
286275 },
287
288 State.ExponentSign => {
276 .ExponentSign => {
289277 if (c == '+') {
290278 i += 1;
291279 } else if (c == '-') {
......@@ -293,20 +281,18 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
293281 i += 1;
294282 }
295283
296 state = State.LeadingExponentZeros;
284 state = .LeadingExponentZeros;
297285 },
298
299 State.LeadingExponentZeros => {
286 .LeadingExponentZeros => {
300287 if (c == '0') {
301288 i += 1;
302289 } else {
303 state = State.Exponent;
290 state = .Exponent;
304291 }
305292 },
306
307 State.Exponent => {
308 if (isDigit(c)) {
309 if (exponent < std.math.maxInt(i32)) {
293 .Exponent => {
294 if (ascii.isDigit(c)) {
295 if (exponent < std.math.maxInt(i32) / 10) {
310296 exponent *= 10;
311297 exponent += @intCast(i32, c - '0');
312298 }
......@@ -323,29 +309,21 @@ fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult {
323309 n.exponent += exponent;
324310
325311 if (n.mantissa == 0) {
326 return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;
312 return if (n.negative) .MinusZero else .PlusZero;
327313 } else if (n.exponent > 309) {
328 return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf;
314 return if (n.negative) .MinusInf else .PlusInf;
329315 } else if (n.exponent < -328) {
330 return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero;
316 return if (n.negative) .MinusZero else .PlusZero;
331317 }
332318
333 return ParseResult.Ok;
334}
335
336inline fn isLower(c: u8) bool {
337 return c -% 'a' < 26;
338}
339
340inline fn toUpper(c: u8) u8 {
341 return if (isLower(c)) (c & 0x5f) else c;
319 return .Ok;
342320}
343321
344322fn caseInEql(a: []const u8, b: []const u8) bool {
345323 if (a.len != b.len) return false;
346324
347325 for (a) |_, i| {
348 if (toUpper(a[i]) != toUpper(b[i])) {
326 if (ascii.toUpper(a[i]) != ascii.toUpper(b[i])) {
349327 return false;
350328 }
351329 }
......@@ -373,11 +351,11 @@ pub fn parseFloat(comptime T: type, s: []const u8) !T {
373351 };
374352
375353 return switch (try parseRepr(s, &r)) {
376 ParseResult.Ok => convertRepr(T, r),
377 ParseResult.PlusZero => 0.0,
378 ParseResult.MinusZero => -@as(T, 0.0),
379 ParseResult.PlusInf => std.math.inf(T),
380 ParseResult.MinusInf => -std.math.inf(T),
354 .Ok => convertRepr(T, r),
355 .PlusZero => 0.0,
356 .MinusZero => -@as(T, 0.0),
357 .PlusInf => std.math.inf(T),
358 .MinusInf => -std.math.inf(T),
381359 };
382360}
383361
......@@ -396,26 +374,28 @@ test "fmt.parseFloat" {
396374 testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc"));
397375
398376 expectEqual(try parseFloat(T, "0"), 0.0);
399 expectEqual((try parseFloat(T, "0")), 0.0);
400 expectEqual((try parseFloat(T, "+0")), 0.0);
401 expectEqual((try parseFloat(T, "-0")), 0.0);
377 expectEqual(try parseFloat(T, "0"), 0.0);
378 expectEqual(try parseFloat(T, "+0"), 0.0);
379 expectEqual(try parseFloat(T, "-0"), 0.0);
402380
403 expectEqual((try parseFloat(T, "0e0")), 0);
404 expectEqual((try parseFloat(T, "2e3")), 2000.0);
405 expectEqual((try parseFloat(T, "1e0")), 1.0);
406 expectEqual((try parseFloat(T, "-2e3")), -2000.0);
407 expectEqual((try parseFloat(T, "-1e0")), -1.0);
408 expectEqual((try parseFloat(T, "1.234e3")), 1234);
381 expectEqual(try parseFloat(T, "0e0"), 0);
382 expectEqual(try parseFloat(T, "2e3"), 2000.0);
383 expectEqual(try parseFloat(T, "1e0"), 1.0);
384 expectEqual(try parseFloat(T, "-2e3"), -2000.0);
385 expectEqual(try parseFloat(T, "-1e0"), -1.0);
386 expectEqual(try parseFloat(T, "1.234e3"), 1234);
409387
410388 expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon));
411389 expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon));
412390
413 expectEqual((try parseFloat(T, "1e-700")), 0);
414 expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T));
391 expectEqual(try parseFloat(T, "1e-700"), 0);
392 expectEqual(try parseFloat(T, "1e+700"), std.math.inf(T));
415393
416394 expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T)));
417 expectEqual((try parseFloat(T, "inF")), std.math.inf(T));
418 expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T));
395 expectEqual(try parseFloat(T, "inF"), std.math.inf(T));
396 expectEqual(try parseFloat(T, "-INF"), -std.math.inf(T));
397
398 expectEqual(try parseFloat(T, "0.4e0066999999999999999999999999999999999999999999999999999"), std.math.inf(T));
419399
420400 if (T != f16) {
421401 expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon));
lib/std/json/test.zig+3-5
......@@ -1751,11 +1751,9 @@ test "i_number_double_huge_neg_exp" {
17511751}
17521752
17531753test "i_number_huge_exp" {
1754 return error.SkipZigTest;
1755 // FIXME Integer overflow in parseFloat
1756 // any(
1757 // \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
1758 // );
1754 any(
1755 \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
1756 );
17591757}
17601758
17611759test "i_number_neg_int_huge_exp" {