چرا عددهای صحیح بزرگ دقت را از دست میدهند
جمعبندی: عددهای صحیح بالاتر از 2^53-1 در JSON.parse دقت را از دست میدهند. شناسههای snowflake، شماره سفارش و bigintهای پایگاهداده همه به این مشکل میخورند، پس آنها را سرتاسر بهصورت رشته نگه دارید.

جمعبندی: شناسههای snowflake، شماره سفارش و کلیدهای اصلی bigint را در سریالایز، انتقال و تجزیه بهصورت رشته بدانید؛ نوع عددی را فقط وقتی نگه دارید که ثابت شده فیلد در بازه 2^53-1 میماند، و آنگاه با محدودیت maximum در Schema اعمالش کنید.
چون آسیب هنگام تجزیه رخ میدهد، بیصدا.
JSON.parse('{"id": 9007199254740993}');
// { id: 9007199254740992 }
رقم گمشده رفته است؛ چیزی برای بازیابی نیست.
2^53 از کجا میآید
جاوااسکریپت یک نوع عددی دارد: دقت دوگانه IEEE 754، با ۵۳ بیت معنادار (یکی ضمنی، ۵۲ صریح). پس عددهای صحیح دقیق در ±(2^53-1) متوقف میشوند:
-9007199254740991 .. 9007199254740991
بالای این خط، مقادیر قابل نمایش متوالی ۲، ۴، ۸ فاصله دارند. 9007199254740993 فرد است و در آن بزرگی هر مقدار قابل نمایش زوج است، پس به 9007199254740992 گرد میشود.
فیلدهایی که گرفتار میشوند
| فیلد | رقمها | ریسک |
|---|---|---|
| شناسههای snowflake | ۱۸–۱۹ | بالا، رقمهای آخر تغییر میکنند |
| شماره سفارش / تراکنش | ۱۶–۲۰ | بالا |
| کلید اصلی bigint پایگاهداده | ۱۹ | بالا |
| پول در واحد خرد | — | پایین |
| مهر زمانی میلیثانیهای | ۱۳ | امن |
نخ مشترک: اینها شناسهاند، نه کمیت. شناسه منتقل میشود و هرگز جمع یا ضرب نمیشود، پس رشته نوع درست است.
چهار اصلاح
بهصورت رشته سریالایز کنید. بکاند فیلدهای bigint را پیش از نوشتن پاسخ تبدیل میکند. ساده و بادوام؛ مستند کنید که این فیلدها رشتهاند.
بگذارید سریالایزر مدیریتش کند. ToStringSerializer جکسون برای Long، تگ json:",string" در Go. همان ایده: مقدار را از مسیر عددی بیرون نگه دارید.
با پشتیبانی BigInt تجزیه کنید. json-bigint و کتابخانههای مشابه. توجه کنید BigInt با حساب Number مخلوط نمیشود و JSON.stringify ردش میکند.
بازه را محدود کنید. اگر فیلد واقعاً نمیتواند از 2^53 بگذرد، maximum را در Schema اضافه کنید تا داده بیرون از بازه در اعتبارسنجی شکست بخورد نه بیصدا در مرورگر.
چگونه بفهمم این باگ را دارم
یک شناسه شناختهشده را رفتوبرگشت کنید: سریالایز، انتقال، تجزیه، مقایسه نویسهبهنویسه. یا بدنه پاسخ خام را بررسی کنید — عدد ۱۹ رقمی بیگیومهای که با رقمهای آخر اشتباه میرسد تقریباً قطعاً افت در زمان تجزیه است.
پرسشهای متداول
چرا JSON.parse رقمهای آخر را بدون خطا تغییر میدهد؟
جاوااسکریپت یک نوع عددی دارد، دقت دوگانه IEEE 754، پس عددهای صحیح بالاتر از 2^53-1 فقط میتوانند روی نزدیکترین مقدار قابل نمایش بنشینند. JSON.parse آن را تبدیل عددی معمولی میداند: نه خطا میدهد نه هشدار.
چگونه فرانتاند میتواند عددهای صحیح بزرگ را دستنخورده نگه دارد؟
از کتابخانهای مانند json-bigint استفاده کنید، چون JSON.parse نمیتواند. هزینهاش این است که BigInt با حساب Number مخلوط نمیشود و JSON.stringify ردش میکند، پس کد اطراف هم باید تغییر کند.
آیا مستندات API باید بگویند این فیلدها رشتهاند؟
بله. سریالایز به رشته سادهترین و بادوامترین اصلاح است، اما فقط وقتی کار میکند که مصرفکنندهها مقدار را متن بدانند، پس نوع فیلد باید در قرارداد API نوشته شود.