JSON Toolkit

Tại sao số nguyên lớn mất độ chính xác

Kết luận: Số nguyên vượt quá 2^53-1 mất độ chính xác trong JSON.parse. ID snowflake, mã đơn hàng và bigint trong cơ sở dữ liệu đều gặp vấn đề này, nên hãy giữ chúng dưới dạng chuỗi từ đầu đến cuối.

Trung cấpĐọc khoảng 4 phútXuất bản 2026-10-30Cập nhật 2026-11-04
Tại sao số nguyên lớn mất độ chính xác trong JSON và cách giữ nguyên chúng

Tóm lại: hãy coi ID snowflake, mã đơn hàng và khóa chính bigint là chuỗi trong suốt quá trình tuần tự hóa, truyền tải và phân tích; chỉ giữ kiểu số khi đã chứng minh được trường đó nằm trong 2^53-1, và khi đó ràng buộc nó bằng maximum trong Schema.

Vì thiệt hại xảy ra ngay trong lúc phân tích, một cách âm thầm.

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

Chữ số bị mất đã biến mất; không có gì để khôi phục.

2^53 đến từ đâu

JavaScript có một kiểu số duy nhất: số thực dấu phẩy động chính xác kép IEEE 754, với 53 bit định trị (một bit ngầm, 52 bit hiện). Vì thế số nguyên chính xác dừng ở ±(2^53-1):

-9007199254740991 .. 9007199254740991

Vượt qua ranh giới đó, các giá trị biểu diễn được liên tiếp cách nhau 2, 4, 8. 9007199254740993 là số lẻ, và ở độ lớn đó mọi giá trị biểu diễn được đều là số chẵn, nên nó bị làm tròn thành 9007199254740992.

Những trường bị ảnh hưởng

Trường Số chữ số Rủi ro
ID snowflake 18–19 Cao, các chữ số cuối thay đổi
Mã đơn hàng / giao dịch 16–20 Cao
Khóa chính bigint trong cơ sở dữ liệu 19 Cao
Tiền theo đơn vị nhỏ nhất — Thấp
Timestamp mili giây 13 An toàn

Điểm chung: đây là định danh, không phải số lượng. Định danh được truyền đi, không bao giờ cộng hay nhân, nên chuỗi là kiểu đúng.

Bốn cách khắc phục

Tuần tự hóa thành chuỗi. Backend chuyển các trường bigint trước khi ghi phản hồi. Đơn giản và bền vững; hãy ghi tài liệu rằng các trường này là chuỗi.

Để bộ tuần tự hóa xử lý. ToStringSerializer của Jackson cho Long, tag json:",string" của Go. Cùng một ý tưởng: giữ giá trị ra khỏi đường đi của kiểu số.

Phân tích với hỗ trợ BigInt. json-bigint và các thư viện tương tự. Lưu ý BigInt không thể trộn với phép tính Number, và JSON.stringify từ chối nó.

Ràng buộc phạm vi. Nếu trường thực sự không thể vượt quá 2^53, hãy thêm maximum trong Schema để dữ liệu ngoài phạm vi thất bại khi kiểm chứng thay vì thất bại âm thầm trong trình duyệt.

Cách biết bạn có lỗi này không

Lấy một ID đã biết và cho nó đi vòng: tuần tự hóa, truyền, phân tích, so sánh từng ký tự. Hoặc kiểm tra phần thân phản hồi thô — một số 19 chữ số không có nháy mà đến với các chữ số cuối sai gần như chắc chắn là mất mát lúc phân tích.

Hỏi đáp

Tại sao JSON.parse đổi các chữ số cuối mà không báo lỗi?

JavaScript chỉ có một kiểu số là số thực dấu phẩy động chính xác kép IEEE 754, nên số nguyên vượt quá 2^53-1 chỉ có thể rơi vào giá trị biểu diễn được gần nhất. JSON.parse coi đó là chuyển đổi số bình thường: nó không ném lỗi cũng không cảnh báo.

Làm sao frontend giữ nguyên số nguyên lớn?

Dùng thư viện như json-bigint, vì JSON.parse không làm được. Cái giá là BigInt không thể trộn với phép tính Number và JSON.stringify từ chối nó, nên mã xung quanh cũng phải thay đổi.

Tài liệu API có nên nói rằng các trường này là chuỗi không?

Có. Tuần tự hóa thành chuỗi là cách sửa đơn giản và bền vững nhất, nhưng nó chỉ hiệu quả nếu phía tiêu thụ coi giá trị là văn bản, nên kiểu của trường phải được ghi vào hợp đồng API.

Đọc thêm