JSON Toolkit

Mengapa bilangan bulat besar kehilangan presisi

Intinya: Bilangan bulat di atas 2^53-1 kehilangan presisi di JSON.parse. Snowflake ID, nomor pesanan dan bigint basis data semuanya terkena, jadi pertahankan sebagai string dari ujung ke ujung.

Menengah±4 menit bacaDiterbitkan 2026-10-30Diperbarui 2026-11-04
Mengapa bilangan bulat besar kehilangan presisi di JSON dan cara mempertahankannya

Intinya: perlakukan snowflake ID, nomor pesanan dan primary key bigint sebagai string sepanjang serialisasi, transport, dan parsing; pertahankan tipe numerik hanya bila field terbukti tetap dalam 2^53-1, dan tegakkan dengan batasan maximum di Schema.

Karena kerusakan terjadi saat parsing, secara diam-diam.

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

Digit yang hilang sudah pergi; tidak ada yang bisa dipulihkan.

Dari mana 2^53 berasal

JavaScript hanya punya satu tipe numerik: IEEE 754 presisi ganda, dengan 53 bit significand (satu implisit, 52 eksplisit). Karena itu bilangan bulat yang tepat berhenti pada ±(2^53-1):

-9007199254740991 .. 9007199254740991

Di atas garis itu, nilai representable yang berurutan berjarak 2, 4, 8. 9007199254740993 ganjil, dan pada besaran itu setiap nilai representable genap, sehingga ia dibulatkan menjadi 9007199254740992.

Field yang terkena

Field Digit Risiko
Snowflake ID 18–19 Tinggi, digit terakhir berubah
Nomor pesanan / transaksi 16–20 Tinggi
Primary key bigint basis data 19 Tinggi
Uang dalam satuan minor — Rendah
Timestamp milidetik 13 Aman

Benang merahnya: ini adalah identifier, bukan kuantitas. Identifier ditransmisikan, tidak pernah ditambah atau dikali, sehingga string adalah tipe yang tepat.

Empat perbaikan

Serialisasi sebagai string. Backend mengonversi field bigint sebelum menulis respons. Sederhana dan tahan lama; dokumentasikan bahwa field tersebut adalah string.

Biarkan serializer menanganinya. ToStringSerializer milik Jackson untuk Long, tag json:",string" milik Go. Ide yang sama: jaga nilai keluar dari jalur numerik.

Parse dengan dukungan BigInt. json-bigint dan pustaka serupa. Perhatikan bahwa BigInt tidak dapat dicampur dengan aritmetika Number, dan JSON.stringify menolaknya.

Batasi rentangnya. Jika field benar-benar tidak dapat melebihi 2^53, tambahkan maximum di Schema agar data di luar rentang gagal validasi alih-alih gagal diam-diam di browser.

Cara mengetahui apakah Anda memiliki bug ini

Ambil ID yang diketahui dan lakukan round-trip: serialisasi, transmisikan, parse, bandingkan karakter demi karakter. Atau periksa body respons mentah — angka 19 digit tanpa tanda kutip yang tiba dengan digit terakhir salah hampir pasti merupakan kehilangan saat parse.

FAQ

Mengapa JSON.parse mengubah digit terakhir tanpa error?

JavaScript hanya memiliki satu tipe numerik, IEEE 754 presisi ganda, sehingga bilangan bulat di atas 2^53-1 hanya dapat mendarat pada nilai representable terdekat. JSON.parse memperlakukannya sebagai konversi numerik biasa: ia tidak melempar error maupun memberi peringatan.

Bagaimana frontend dapat mempertahankan bilangan bulat besar secara utuh?

Gunakan pustaka seperti json-bigint, karena JSON.parse tidak dapat melakukannya. Biayanya adalah BigInt tidak dapat dicampur dengan aritmetika Number dan JSON.stringify menolaknya, sehingga kode di sekitarnya juga harus berubah.

Haruskah dokumentasi API menyatakan bahwa field ini adalah string?

Ya. Serialisasi ke string adalah perbaikan paling sederhana dan paling tahan lama, tetapi hanya berhasil jika konsumen memperlakukan nilainya sebagai teks, sehingga tipe field harus dituliskan dalam kontrak API.

Bacaan lanjutan