JSON Toolkit

Proč velká celá čísla ztrácejí přesnost

Shrnutí: Celá čísla nad 2^53-1 ztrácejí přesnost v `JSON.parse`. Snowflake ID, čísla objednávek i bigint primární klíče databází to zasahuje, proto je udržujte jako řetězce od začátku do konce.

Středně pokročilý~4 min čteníPublikováno 2026-10-30Aktualizováno 2026-11-04
Proč velká celá čísla v JSON ztrácejí přesnost a jak je udržet

Stručně: považujte snowflake ID, čísla objednávek a bigint primární klíče za řetězce napříč serializací, přenosem i parsováním; číselný typ ponechte jen tehdy, když je prokázáno, že pole zůstává v rozsahu 2^53-1, a pak ho vynuťte omezením maximum ve Schema.

Protože škoda vzniká během parsování, tiše.

JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 }

Chybějící číslice je pryč; není co obnovit.

Odkud pochází 2^53

JavaScript má jeden číselný typ: IEEE 754 s dvojitou přesností, s 53 bity mantissy (jeden implicitní, 52 explicitních). Přesná celá čísla proto končí na ±(2^53-1):

-9007199254740991 .. 9007199254740991

Nad touto hranicí jsou po sobě jdoucí reprezentovatelné hodnoty vzdálené 2, 4, 8. 9007199254740993 je liché a v této velikosti je každá reprezentovatelná hodnota sudá, takže se zaokrouhlí na 9007199254740992.

Pole, která to zasáhne

Pole Číslice Riziko
Snowflake ID 18–19 Vysoké, poslední číslice se mění
Čísla objednávek / transakcí 16–20 Vysoké
Bigint primární klíče databází 19 Vysoké
Peníze v nejmenších jednotkách — Nízké
Časová razítka v milisekundách 13 Bezpečné

Společný motiv: jde o identifikátory, nikoli množství. Identifikátor se přenáší, nikdy se nesčítá ani nenásobí, takže správný typ je řetězec.

Čtyři opravy

Serializujte jako řetězec. Backend převede bigint pole před zápisem odpovědi. Jednoduché a trvanlivé; zdokumentujte, že tato pole jsou řetězce.

Nechte to na serializátoru. Jacksonův ToStringSerializer pro Long, tag json:",string" v Go. Stejná myšlenka: držte hodnotu mimo číselnou cestu.

Parsujte s podporou BigInt. json-bigint a podobné knihovny. Pozor, BigInt se nesmí míchat s aritmetikou Number a JSON.stringify ho odmítá.

Omezte rozsah. Pokud pole skutečně nemůže přesáhnout 2^53, přidejte ve Schema maximum, aby data mimo rozsah selhala validací, místo aby tiše selhala v prohlížeči.

Jak zjistit, zda máte tuto chybu

Vezměte známé ID a projděte ho obousměrným převodem: serializujte, přeneste, parsujte, porovnejte znak po znaku. Nebo prozkoumejte surové tělo odpovědi – necitované 19ciferné číslo, které dorazí se špatnými posledními číslicemi, je téměř jistě ztráta při parsování.

Časté dotazy

Proč JSON.parse mění poslední číslice bez chyby?

JavaScript má jeden číselný typ, IEEE 754 s dvojitou přesností, takže celá čísla nad 2^53-1 mohou přistát jen na nejbližší reprezentovatelné hodnotě. JSON.parse to považuje za normální číselný převod: ani nevyhodí chybu, ani nevaruje.

Jak může frontend udržet velká celá čísla neporušená?

Použijte knihovnu jako json-bigint, protože JSON.parse to neumí. Cenou je, že BigInt se nesmí míchat s aritmetikou Number a JSON.stringify ho odmítá, takže se musí změnit i okolní kód.

Mají API dokumenty uvádět, že tato pole jsou řetězce?

Ano. Serializace na řetězce je nejjednodušší a nejtrvanlivější oprava, ale funguje jen tehdy, když konzumenti hodnotu považují za text, takže typ pole musí být zapsán ve smlouvě API.

Další čtení