เปิด console ของ JavaScript ในเบราว์เซอร์แล้วพิมพ์ 0.1 + 0.2 — แทนที่จะได้ 0.3 กลับได้ 0.30000000000000004 นี่ไม่ใช่บั๊กของ JavaScript โดยเฉพาะ แต่เป็นผลโดยตรงจากวิธีที่มาตรฐาน IEEE 754 แทนค่าตัวเลขทศนิยมในระบบฐานสอง และผลลัพธ์แบบเดียวกันนี้ก็เกิดขึ้นใน PHP, Java, Python และแทบทุกภาษาที่ใช้มาตรฐานนี้
สามส่วนของตัวเลขทศนิยม
ตัวเลขตาม IEEE 754 ถูกเก็บเป็นสามส่วนประกอบ: เครื่องหมาย (บวกหรือลบ) แมนทิสซา (หลักที่มีนัยสำคัญของตัวเลข) และเลขชี้กำลัง (กำลังที่จุดทศนิยมถูกเลื่อน) คล้ายกับสัญกรณ์วิทยาศาสตร์: 1.5 × 10² เพียงแต่เป็นระบบฐานสอง
เหตุใด 0.1 จึงไม่สามารถแทนค่าได้แม่นยำในระบบฐานสอง
0.1 ในระบบฐานสิบเป็นเศษส่วนไม่สิ้นสุดในระบบฐานสอง (คล้ายกับที่ 1/3 ไม่สิ้นสุดในระบบฐานสิบ) คอมพิวเตอร์เก็บบิตแมนทิสซาได้จำนวนจำกัดเท่านั้น ดังนั้นตัวเลขจึงถูกปัดเศษเป็นค่าที่ใกล้เคียงที่สุดที่แทนค่าได้ — และการปัดเศษนี้เองที่ก่อให้เกิด "ข้อผิดพลาด" ที่มองเห็นได้
จุดที่สร้างปัญหาจริง: ยอดรวมตะกร้าสินค้า
หน้าชำระเงินที่บวกราคาสินค้าแต่ละชิ้นเป็น float ธรรมดาอาจได้ยอดรวมคลาดเคลื่อนไปสองสามสตางค์จากที่ควรจะเป็น หลังจากบวกสินค้าหลายรายการ — การบวกแต่ละครั้งปัดเศษเล็กน้อย และข้อผิดพลาดสะสมแทนที่จะหักล้างกัน นี่คือเหตุผลที่ระบบชำระเงินมักคำนวณเป็นสตางค์แบบจำนวนเต็ม หรือใช้ type decimal เฉพาะทาง แทนที่จะบวก float ตรง ๆ
ทำไมต้องใช้สิ่งนี้
- เข้าใจและวินิจฉัยข้อผิดพลาดจากการปัดเศษที่ไม่คาดคิดในการคำนวณทางการเงินหรือวิทยาศาสตร์
- ดูการแทนค่าฐานสองที่แม่นยำของตัวเลขทศนิยมที่ระบุ
- อธิบายให้เพื่อนร่วมงานหรือนักเรียนฟังว่าเหตุใดการเปรียบเทียบตัวเลขทศนิยมด้วยความเท่ากันแบบแน่นอนจึงเป็นวิธีปฏิบัติที่ไม่ดี
เลขซับนอร์มัลใกล้ศูนย์
เมื่อค่าของตัวเลขมีขนาดเล็กเกินไปสำหรับการแสดงผลปกติ (แมนทิสซาที่เริ่มต้นด้วยเลข 1 โดยนัย) IEEE 754 จะเปลี่ยนไปใช้โหมดซับนอร์มัล (denormalized) ซึ่งเลข 1 โดยนัยนี้จะถูกละทิ้ง — สิ่งนี้ช่วยให้แสดงตัวเลขที่มีขนาดเล็กลงไปอีกได้ โดยแลกกับการสูญเสียความละเอียดอย่างค่อยเป็นค่อยไปแทนที่จะกระโดดไปที่ศูนย์ทันที การเปลี่ยนผ่านไปสู่ศูนย์อย่าง "นุ่มนวล" นี้ดีกว่าการเป็นศูนย์ทันที แต่การคำนวณแบบซับนอร์มัลจะช้ากว่าการคำนวณปกติอย่างเห็นได้ชัดในโปรเซสเซอร์บางรุ่น