ข้ามไปเนื้อหาหลัก

เจาะลึกปัญหา JSON.parse ปัดเศษตัวเลขนาโนวินาทีพลาด

เมื่อไทม์สปริงนาโนวินาทีบนเซิร์ฟเวอร์ Python ถูกส่งเป็น JSON ไปยังเบราว์เซอร์ Node.js v22.23.2 แล้วค่าความแม่นยำหายไปเพราะเกินขีดจำกัด MAX_SAFE_INTEGER จนทำให้ระบบตรวจสอบไฟล์ผิดพลาด

เรียบเรียงโดย AI
Inewgen
20 Oct 2026ที่มา: Dev.to3 นาทีอ่าน (0 ครั้ง)
แชร์
เจาะลึกปัญหา JSON.parse ปัดเศษตัวเลขนาโนวินาทีพลาด

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง

ขนาดตัวอักษร
  • เซิร์ฟเวอร์ Python ส่งไทม์สแตมป์นาโนวินาทีเป็น JSON เพื่อระบุเวลาแก้ไขไฟล์
  • ค่าตัวเลขยาวเกินกว่าขีดจำกัด MAX_SAFE_INTEGER ทำให้เบราว์เซอร์เกิดความผิดพลาดในการประมวลผลผ่าน JSON.parse
  • ฟีเจอร์ใหม่อย่างตัวเลือก source ใน JSON.parse reviver ช่วยตรวจสอบและป้องกันข้อผิดพลาดได้
  • ทางแก้ประกอบด้วยการแปลงเป็นสตริง การย่อเป็นมิลลิวินาที หรือใช้ Hash เพื่อความแม่นยำ

ปัญหาความแม่นยำของตัวเลขในระบบไอทีเกิดขึ้นได้จากหลายปัจจัย ล่าสุดมีการเปิดเผยเคสตัวอย่างที่น่าสนใจบนเครื่องลินุกซ์ด้วยเคอร์เนล 6.8, Python 3.10.12 และ Node.js v22.23.2 โดยเริ่มต้นจากเซิร์ฟเวอร์ Python ขนาดเล็กทำหน้าที่แสดงรายการไฟล์คลิปวิดีโอในรูปแบบ JSON พร้อมระบุเวลาการแก้ไขไฟล์ (Modification Time) เป็นหน่วยนาโนวินาที

ทว่าหน้าเว็บเบราว์เซอร์ที่อ่านรายการดังกล่าวกลับพบว่าไฟล์สามไฟล์มีเวลาใกล้เคียงกันมาก โดยสคริปต์ทดสอบได้สร้างไฟล์เปล่าสามไฟล์และกำหนดเวลาห่างกันเพียง 100 นาโนวินาทีด้วยคำสั่ง os.utime(ns=...) เพื่อให้ผลลัพธ์คงที่ในทุกการรัน ปัญหาบังเกิดเมื่อเบราว์เซอร์ส่งไทม์สแตมป์กลับไปถามเซิร์ฟเวอร์ว่าไฟล์นี้มีการเปลี่ยนแปลงหรือไม่ ซึ่งผลลัพธ์คือเซิร์ฟเวอร์ตอบกลับว่ามีการเปลี่ยนแปลง ทั้งที่ไม่มีไฟล์ใดถูกแก้ไขเลย ตัวเลขเหล่านั้นมีขนาดใหญ่เกินกว่าที่ฟังก์ชัน JSON.parse ในเบราว์เซอร์จะรองรับได้อย่างแม่นยำ

คำว่าปลอดภัยในบริบทนี้หมายถึงความสามารถในการแสดงผลจำนวนเต็มได้อย่างแม่นยำและเปรียบเทียบได้อย่างถูกต้อง โดยไทม์สแตมป์ระดับนาโนวินาทีของปีนี้มีขนาดใหญ่กว่าขีดจำกัดมาตรฐานมาก จนฟังก์ชัน math.ulp ของ Python สามารถคำนวณระยะห่างระหว่างเลขทศนิยมใกล้เคียงกันในขนาดดังกล่าวได้

javascript code editor programming background

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง

เมื่อเบราว์เซอร์พยายามส่งค่ากลับไปตรวจสอบ เช่น c.mp4 เพื่อเช็คว่าไฟล์เปลี่ยนไปจากเดิมหรือไม่ ค่าที่ส่งกลับกลายเป็นตัวเลขที่ถูกปัดเศษ ทำให้เกิดความผิดพลาดแบบหนังคนละม้วน เช่น a.mp4 และ b.mp4 กลายเป็นค่าเดียวกันบนหน้าเว็บ ทำให้ระบบทดสอบการอัปเดตไม่สามารถแยกแยะความแตกต่างได้

ในทางเทคนิค ข้อจำกัดนี้มาจากมาตรฐานของภาษา JavaScript ที่ใช้ตัวเลขแบบ Double-precision floating-point (IEEE 754) ซึ่งสามารถเก็บจำนวนเต็มที่แม่นยำได้สูงสุดถึง 2^53 - 1 หรือที่รู้จักในชื่อ Number.MAX_SAFE_INTEGER (9,007,199,254,740,991) เมื่อไทม์สแตมป์หน่วยนาโนวินาทีพุ่งสูงเกิน 19 หลัก ตัวเลขหลักท้ายจึงถูกปัดทิ้งโดยอัตโนมัติระหว่างกระบวนการแปลงข้อมูล JSON ทำให้ระบบหลังบ้านและหน้าบ้านมองเห็นข้อมูลไม่ตรงกัน แม้โค้ดจะถูกต้องก็ตาม

เอกสารจาก MDN เกี่ยวกับ JSON.parse ระบุสาเหตุไว้อย่างชัดเจนว่าฟังก์ชัน reviver จะทำงานหลังจากที่ค่าถูกแปลงเสร็จสิ้นแล้ว ตัวเลขใน JSON จึงถูกแปลงเป็นตัวเลขของ JavaScript ไปเรียบร้อยและสูญเสียความแม่นยำในกระบวนการดังกล่าว

ไม่อยากพลาดข่าวใหม่?

สมัครรับสรุปข่าวสารใหม่ทางอีเมล ไม่บ่อยจนรำคาญ

โฆษณา

อย่างไรก็ตาม Node 22 รองรับอาร์กิวเมนต์ตัวที่สามของ reviver ซึ่งมีฟิลด์ source เก็บข้อความต้นฉบับของค่านั้น ทำให้นักพัฒนาสามารถตรวจสอบเทียบเคียงข้อความต้นฉบับกับผลลัพธ์ของตัว parser ได้ โดยใช้รูปแบบ BigInt(context.source) เพื่อสร้างค่าใหม่ที่ถูกต้องขึ้นมาแทนการใช้ตัวเลขที่ถูกปัดเศษทิ้ง

นอกจากการใช้ reviver แล้ว ยังมีแนวทางอื่นในการแก้ปัญหา เช่น:

  • ส่งค่าดังกล่าวในรูปแบบสตริง (String) เพื่อให้รอดพ้นจากการปัดเศษของ JSON parser ทุกยุคทุกสมัย
  • ย่อสเกลลงเป็นหน่วยมิลลิวินาที (หารด้วย 1,000,000) ซึ่งทำให้ตัวเลขตกลงมาอยู่ในเกณฑ์ปลอดภัยที่เบราว์เซอร์ทุกตัวรองรับได้

อย่างไรก็ดี การลดทอนเหลือระดับมิลลิวินาทีก็มีข้อแลกเปลี่ยน เพราะหากไฟล์ถูกแก้ไขภายในเสี้ยววินาทีเดียวกัน ระบบตรวจสอบจะไม่สามารถจับความเปลี่ยนแปลงนั้นได้ หากเซิร์ฟเวอร์ต้องการความแม่นยำสูงสุด การใช้ Content Hash หรือตัวนับเวอร์ชันในรูปแบบสตริงจึงเป็นทางเลือกที่ปลอดภัยกว่า

ที่มา: Dev.to

ความคิดเห็น

แสดงความคิดเห็น
0/2000

พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้