Skip to content

JSON DirectParser parses floats differently from the tape materializer — and node agrees with the tape #7477

Description

@proggeramlug

The DirectParser and the tape materializer produce different values from the same JSON text, and node agrees with the tape. On benchmarks/json_polyglot/bench_field_access.ts (10k records, values like i * 3.14159, checksum = sum of nested.x + stringify lengths over 50 iterations):

parse path checksum
node v22.23.1 2552985550
tape materializer (PERRY_JSON_TAPE=1 / auto) 2552985550
DirectParser (PERRY_JSON_TAPE=0) 2552986400

Reproduce: PERRY_JSON_TAPE=0 vs =1 on that benchmark; the divergence is deterministic and survives PERRY_GEN_GC=0, so it is the parser, not GC.

Why it matters beyond the flag

  1. The DirectParser is the default for real workloads: every blob under 1 KB and over 16 MB, plus every non-top-level-array shape, parses through it (try_parse_via_tape's auto window). Those all get the divergent values today, silently.
  2. It blocks the most promising field_access optimization. force_materialize_lazy could re-parse the retained blob with the DirectParser (~2.3× faster than the per-element tape materializer — see the companion perf issue), and a prototype of exactly that produced the wrong checksum because of this divergence. Verified: swapping the materializer for the DirectParser flips the checksum from node-matching to divergent.

Where to look

Both paths parse JSON numbers from the same bytes, so one of the two float-parsing routines rounds differently (likely a hand-rolled decimal-to-f64 in one path vs a stricter algorithm in the other). Since node (V8's strtod) agrees with the tape side, the DirectParser's number parsing is the suspect. stringify lengths also feed the checksum, so a float→string difference downstream of parse is the alternative — distinguishing the two is the first step: sum nested.x alone (pure parse) vs stringify-length alone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions