Mengapa integer besar kehilangan ketepatan
Inti pati: Integer melebihi 2^53-1 kehilangan ketepatan dalam JSON.parse. ID snowflake, nombor pesanan dan bigint pangkalan data semuanya terjejas, jadi biarkan mereka sebagai rentetan dari hujung ke hujung.

Pokoknya: perlakukan ID snowflake, nombor pesanan dan kunci utama bigint sebagai rentetan melalui pensirian, pengangkutan dan penghuraian; kekalkan jenis berangka hanya apabila medan terbukti kekal dalam 2^53-1, dan kemudian kuatkuasakannya dengan kekangan maximum dalam Schema.
Kerana kerosakan berlaku semasa penghuraian, secara senyap.
JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 }
Digit yang hilang sudah tiada; tiada apa yang boleh dipulihkan.
Dari mana 2^53 datang
JavaScript mempunyai satu jenis berangka: ketepatan berganda IEEE 754, dengan 53 bit signifikan (satu tersirat, 52 eksplisit). Integer tepat oleh itu berhenti pada ±(2^53-1):
-9007199254740991 .. 9007199254740991
Di atas garis itu, nilai boleh wakil berturut-turut terpisah 2, 4, 8. 9007199254740993 ialah ganjil, dan pada magnitud itu setiap nilai boleh wakil adalah genap, jadi ia dibundarkan kepada 9007199254740992.
Medan yang terjejas
| Medan | Digit | Risiko |
|---|---|---|
| ID snowflake | 18–19 | Tinggi, digit akhir berubah |
| Nombor pesanan / transaksi | 16–20 | Tinggi |
| Kunci utama bigint pangkalan data | 19 | Tinggi |
| Wang dalam unit kecil | — | Rendah |
| Cap masa milisaat | 13 | Selamat |
Benang umum: ini ialah pengecam, bukan kuantiti. Pengecam diangkut, tidak pernah ditambah atau didarab, jadi rentetan ialah jenis yang betul.
Empat pembetulan
Sirikan sebagai rentetan. Backend menukar medan bigint sebelum menulis respons. Mudah dan tahan lama; dokumentasikan bahawa medan ini ialah rentetan.
Biarkan pensirian mengendalikannya. ToStringSerializer Jackson untuk Long, tag json:",string" Go. Idea yang sama: jauhkan nilai daripada laluan berangka.
Huraikan dengan sokongan BigInt. json-bigint dan pustaka serupa. Perhatikan bahawa BigInt tidak boleh dicampur dengan aritmetik Number, dan JSON.stringify menolaknya.
Kekang julat. Jika medan benar-benar tidak boleh melebihi 2^53, tambah maximum dalam Schema supaya data di luar julat gagal pengesahan dan bukannya gagal secara senyap dalam pelayar.
Bagaimana mengetahui sama ada anda mempunyai bug ini
Ambil ID yang diketahui dan putarkannya: sirikan, hantar, huraikan, bandingkan aksara demi aksara. Atau periksa badan respons mentah — nombor 19 digit tanpa petikan yang tiba dengan digit akhir salah hampir pasti kehilangan semasa penghuraian.
Soalan Lazim
Mengapa JSON.parse mengubah digit akhir tanpa ralat?
JavaScript mempunyai satu jenis berangka, ketepatan berganda IEEE 754, jadi integer melebihi 2^53-1 hanya boleh mendarat pada nilai boleh wakil terdekat. JSON.parse memperlakukannya sebagai penukaran berangka biasa: ia tidak melontar mahupun memberi amaran.
Bagaimana frontend boleh mengekalkan integer besar tanpa kerosakan?
Gunakan pustaka seperti json-bigint, kerana JSON.parse tidak boleh melakukannya. Kosnya ialah BigInt tidak boleh dicampur dengan aritmetik Number dan JSON.stringify menolaknya, jadi kod sekeliling juga perlu berubah.
Patutkah dokumentasi API menyatakan bahawa medan ini ialah rentetan?
Ya. Pensirian kepada rentetan ialah pembetulan paling mudah dan paling tahan lama, tetapi ia hanya berkesan jika pengguna memperlakukan nilai sebagai teks, jadi jenis medan perlu ditulis ke dalam kontrak API.