Skip to content

Large integers print exact value instead of V8 shortest round-trip (2**58 → …744 vs …740) #6127

Description

@proggeramlug

Severity: P2 (wrong string output; value is correct)
Found while addressing a CodeRabbit note on #6123 (v0.5.1255).

What

Large integers where several integers map to the same f64 are printed as the exact integer value instead of V8's shortest round-trip decimal.

console.log(2 ** 58); // node: 288230376151711740   perry: 288230376151711744
console.log(2 ** 60); // node: 1152921504606847000  perry: 1152921504606846976

The stored double is identical (Number("0x3fffffffffffff8") === 2**58 is true, bits 4390000000000000) — only the Number → String rendering differs. Node/V8 print the shortest decimal that round-trips to the double; Perry prints the full integer, which has more significant digits than round-tripping requires.

Why

js_format_f64's integer path (magnitudes below the 1e21 exponential threshold, crates/perry-runtime/src/value/to_string.rs) renders the exact integer rather than applying the shortest-round-trip (Grisu/Ryū) algorithm that the fractional path uses. For integers < 2^53 every integer is representable so the exact form is shortest; at ≥ 2^53 the double's shortest decimal can have trailing zeros the exact integer doesn't.

Repro (characterization)

Matches node: 2**53, 2**54, 1e16, 9007199254740994. Diverges: 2**58, 2**60 (and other magnitudes whose nearest double's shortest decimal ends in zeros).

Fix direction

Route the integer branch through the same shortest-round-trip digit generator as the fractional/exponential paths (Perry already has spec_to_exponential), or apply shortest-round-trip whenever |v| >= 2^53.

Note

Number() parsing of such literals is correct (produces the right double) — this is purely the print side. Confirmed via test_gap_number_nondecimal_overflow which asserts the doubles with ===.

Confidence: high (probe + source).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions