From 1cce539ddcba1e21615e11dcf75f011074942414 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 6 Aug 2023 00:17:37 -0400 Subject: [PATCH] json.stringify: properly implement RFC8259 recommendation The previous magic numbers used `1 << 52`, which did not account for the implicit leading one in the floating point format. The RFC is correct when it uses an exponent of 53. Technically these exclusive endpoints are also representable, but everyone including the RFC seems to use them exclusively. Also, delete special case optimizations related to the type which have already been implemented in the zig compiler to produce comptime values for tautological runtime comparisons. --- lib/std/json/stringify.zig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/std/json/stringify.zig b/lib/std/json/stringify.zig index aa719e620b..81a09d26ba 100644 --- a/lib/std/json/stringify.zig +++ b/lib/std/json/stringify.zig @@ -34,7 +34,7 @@ pub const StringifyOptions = struct { /// Should unicode characters be escaped in strings? escape_unicode: bool = false, - /// When true, renders numbers outside the range `±1<<53` (the precise integer range of f64) as JSON strings in base 10. + /// When true, renders numbers outside the range `+-1<<53` (the precise integer range of f64) as JSON strings in base 10. emit_big_numbers_quoted: bool = false, }; @@ -164,7 +164,7 @@ pub fn writeStreamArbitraryDepth( /// * Zig `bool` -> JSON `true` or `false`. /// * Zig `?T` -> `null` or the rendering of `T`. /// * Zig `i32`, `u64`, etc. -> JSON number or string. -/// * If the value is outside the range `±1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. +/// * If the value is outside the range `+-1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. /// * Zig floats -> JSON number or string. /// * If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number. /// * TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00". @@ -402,13 +402,11 @@ pub fn WriteStream( pub fn write(self: *Self, value: anytype) Error!void { const T = @TypeOf(value); switch (@typeInfo(T)) { - .Int => |info| { - const emit_unquoted = - if (!self.options.emit_big_numbers_quoted) true - else if (info.bits < 53) true - else (value < 4503599627370496 and (info.signedness == .unsigned or value > -4503599627370496)); + .Int => { try self.valueStart(); - if (emit_unquoted) { + if (!self.options.emit_big_numbers_quoted or + (value > -(1 << 53) and value < (1 << 53))) + { try self.stream.print("{}", .{value}); } else { try self.stream.print("\"{}\"", .{value});