ถ้าผมถามคุณว่า 0.1 + 0.2 คืออะไร? ให้ฉันมองเปล่า ๆ ก็ได้ 0.1 + 0.2 = 0.3 อ่า ยังต้องถามอีกเหรอ? แม้แต่เด็กในโรงเรียนอนุบาลก็สามารถตอบคำถามเด็กเช่นนี้ได้ แต่คุณรู้ไหมว่าปัญหาเดียวกันในภาษาการเขียนโปรแกรมอาจไม่ง่ายอย่างที่คิด
ไม่เชื่อเหรอ? มาดูชิ้นส่วนของ JS ก่อน
ตัวแปร numA = 0.1;
var numB = 0.2;
การแจ้งเตือน ( (numA + numB) === 0.3 );
ผลการดำเนินการเป็นเท็จ ใช่ เมื่อฉันเห็นโค้ดนี้เป็นครั้งแรก ฉันยอมรับว่ามันเป็นความจริง แต่ผลการดำเนินการทำให้ฉันประหลาดใจ วิธีการเปิดของฉันผิดหรือเปล่า? ไม่ ไม่ ลองรันโค้ดต่อไปนี้อีกครั้ง แล้วเราจะรู้ว่าเหตุใดผลลัพธ์จึงเป็นเท็จ
ตัวแปร numA = 0.1;
var numB = 0.2;
การแจ้งเตือน (numA + numB);
ปรากฎว่า 0.1 + 0.2 = 0.30000000000000004 มันไม่แปลกเหรอ? ในความเป็นจริง สำหรับการดำเนินการทางคณิตศาสตร์สี่จุดของตัวเลขทศนิยม ภาษาการเขียนโปรแกรมเกือบทั้งหมดจะมีปัญหาคล้ายกับข้อผิดพลาดด้านความแม่นยำ อย่างไรก็ตาม ในภาษาต่างๆ เช่น C++/C#/Java วิธีการต่างๆ ได้ถูกห่อหุ้มไว้เพื่อหลีกเลี่ยงปัญหาด้านความแม่นยำ และ JavaScript เป็นประเภทที่อ่อนแอ ภาษาไม่มีประเภทข้อมูลที่เข้มงวดสำหรับตัวเลขทศนิยมจากแนวคิดการออกแบบ ดังนั้นปัญหาข้อผิดพลาดที่แม่นยำจึงมีความโดดเด่นเป็นพิเศษ มาวิเคราะห์ว่าเหตุใดจึงมีข้อผิดพลาดด้านความแม่นยำและวิธีแก้ไข
ก่อนอื่น เราต้องคิดถึงปัญหาที่ดูเหมือนเด็กที่ 0.1 + 0.2 จากมุมมองของคอมพิวเตอร์ เรารู้ว่าสิ่งที่คอมพิวเตอร์อ่านได้นั้นเป็นไบนารี่ ไม่ใช่ทศนิยม ดังนั้นก่อนอื่นเรามาแปลง 0.1 และ 0.2 เป็นไบนารี่ก่อนแล้วลองดู:
0.1 => 0.0001 1001 1001 1001… (วนซ้ำไม่สิ้นสุด)
0.2 => 0.0011 0011 0011 0011… (วนซ้ำไม่สิ้นสุด)
ส่วนทศนิยมของเลขทศนิยมที่มีความแม่นยำสองเท่ารองรับได้มากถึง 52 บิต ดังนั้นหลังจากเพิ่มทั้งสองเข้าไปแล้ว เราจะได้สตริง 0.0100110011001100110011001100110011001100110011001100 เลขฐานสองถูกตัดทอนเนื่องจากการจำกัดตำแหน่งทศนิยมของตัวเลขทศนิยม ในขณะนี้ เราแปลงเป็นทศนิยมและกลายเป็น 0.30000000000000004
แค่นั้นแหละ แล้วจะแก้ไขปัญหานี้อย่างไร? ผลลัพธ์ที่อยากได้คือ 0.1 + 0.2 === 0.3 อ๋อ! - -
วิธีแก้ไขที่ง่ายที่สุดวิธีหนึ่งคือการระบุข้อกำหนดด้านความแม่นยำที่ชัดเจน ในกระบวนการส่งคืนค่า คอมพิวเตอร์จะปัดเศษโดยอัตโนมัติ เช่น:
ตัวแปร numA = 0.1;
var numB = 0.2;
การแจ้งเตือน ( parseFloat ((numA + numB).toFixed(2)) === 0.3 );
แต่เห็นได้ชัดว่านี่ไม่ใช่ครั้งเดียวและสำหรับทุกวิธี คงจะดีมากถ้ามีวิธีที่สามารถช่วยเราแก้ปัญหาความแม่นยำของจำนวนจุดลอยตัวเหล่านี้ได้ ลองใช้วิธีนี้:
Math.formatFloat = ฟังก์ชั่น (f, หลัก) {
var m = Math.pow (10, หลัก);
กลับ parseInt(f * m, 10) / m;
-
ตัวแปร numA = 0.1;
var numB = 0.2;
การแจ้งเตือน (Math.formatFloat (numA + numB, 1) === 0.3);
วิธีนี้หมายถึงอะไร? เพื่อหลีกเลี่ยงความแตกต่างที่แม่นยำ เราจำเป็นต้องคูณตัวเลขที่จะคำนวณด้วย 10 ยกกำลัง n แล้วแปลงเป็นจำนวนเต็มที่คอมพิวเตอร์สามารถจดจำได้อย่างแม่นยำ แล้วหารด้วย 10 ยกกำลัง n นี่คือวิธีที่ถูกต้องที่สุด ภาษาการเขียนโปรแกรมจัดการกับความแตกต่างที่มีความแม่นยำ เราจะใช้เพื่อจัดการกับข้อผิดพลาดที่แม่นยำของตัวเลขทศนิยมใน JS
ถ้ามีคนถามคุณครั้งหน้าว่า 0.1 + 0.2 เท่ากับอะไร คำตอบของคุณก็ควรระวัง! -