มาดูคำถามสองข้อก่อน:
0.1 + 0.2 == 0.3; // เท็จ
999999999999999999 == 100000000000000000; // จริง
ปัญหาแรกคือความถูกต้องของทศนิยมซึ่งได้กล่าวถึงในหลายบล็อกในอุตสาหกรรม ปัญหาที่สองคือปีที่แล้วเมื่อฐานข้อมูลระบบกำลังแก้ไขข้อมูลพบว่าข้อมูลบางอย่างถูกทำซ้ำ บทความนี้จะเริ่มต้นจากบรรทัดฐานและสรุปปัญหาข้างต้น
จำนวนเต็มสูงสุด
ตัวเลขใน JavaScript ถูกเก็บไว้โดยใช้หมายเลขจุดลอยตัวของ IEEE 754 Double Precision 64 บิตและรูปแบบของพวกเขาคือ:
sxmx 2^e
S เป็นบิตสัญญาณบ่งบอกถึงบวกและลบ M คือ Mantissa ที่มี 52 บิต E คือเลขชี้กำลังโดยมี 11 บิต ในข้อกำหนด ECMASCRIPT ช่วงที่กำหนดโดย E คือ [-1074, 971] ด้วยวิธีนี้มันเป็นเรื่องง่ายที่จะอนุมานได้ว่าจำนวนเต็มสูงสุดที่ JavaScript สามารถเป็นตัวแทนได้คือ:
1 x (2^53 - 1) x 2^971 = 1.7976931348623157E+308
ค่านี้คือ number.max_value
ในทำนองเดียวกันค่าของ number.min_value สามารถอนุมานได้เป็น:
1 x 1 x 2^(-1074) = 5e-324
โปรดทราบว่า min_value หมายถึงจำนวนบวกที่ใกล้เคียงกับ 0 มากที่สุดไม่ใช่จำนวนที่น้อยที่สุด จำนวนที่เล็กที่สุดคือ -number.max_value
ความถูกต้องของทศนิยมหายไป
หมายเลข JavaScript เป็นตัวเลขลอยความแม่นยำสองเท่าและเก็บไว้ในไบนารีในคอมพิวเตอร์ เมื่อจำนวนบิตที่สำคัญเกิน 52 บิตจะมีการสูญเสียความแม่นยำ ตัวอย่างเช่น:
ไบนารีของทศนิยม 0.1 คือ 0.0 0011 0011 0011 … (วนรอบ 0011)
ไบนารีของทศนิยม 0.2 คือ 0.0011 0011 0011 … (วนรอบ 0011)
0.1 + 0.2 สามารถแสดงเป็น:
e = -4; M = 1.10011001100 ... 1100 (52 บิต)
+ e = -3; M = 1.10011001100 ... 1100 (52 บิต)
-
e = -3; M = 0.11001100110 ... 0110
+ e = -3; M = 1.10011001100 ... 1100
-
e = -3; M = 10.01100110011 ... 001
-
= 0.01001100110011 ... 001
= 0.3000000000000000004 (ทศนิยม)
จากการคำนวณข้างต้นเรายังสามารถสรุปได้ว่า: เมื่อจำนวนทศนิยมทศนิยมจำนวน จำกัด ในการเป็นตัวแทนไบนารีไม่เกิน 52 บิตมันสามารถเก็บไว้ได้อย่างถูกต้องใน JavaScript ตัวอย่างเช่น:
0.05 + 0.005 == 0.055 // จริง
กฎเพิ่มเติมเช่น:
0.05 + 0.2 == 0.25 // จริง
0.05 + 0.9 == 0.95 // เท็จ
ต้องพิจารณาโหมดการปัดเศษของ IEEE 754 และผู้ที่สนใจสามารถศึกษาเพิ่มเติมได้
ความแม่นยำของจำนวนเต็มขนาดใหญ่หายไป
คำถามนี้ไม่ค่อยมีการกล่าวถึง ก่อนอื่นเราต้องทราบว่าปัญหาคืออะไร:
1. จำนวนเต็มสูงสุดที่ JavaScript สามารถจัดเก็บได้คืออะไร?
คำถามนี้ได้รับคำตอบก่อนหน้านี้และเป็น number.max_value จำนวนมากมาก
2. จำนวนเต็มสูงสุดที่ JavaScript สามารถจัดเก็บได้โดยไม่สูญเสียความแม่นยำ?
ตาม SXMX 2^E บิตสัญญาณเป็นค่าบวก Mantissa 52 บิตนั้นเต็มไปด้วย 1 และ Exponent E คือค่าสูงสุดของ 971 เห็นได้ชัดว่าคำตอบยังคงเป็น number.max_value
ปัญหาของเราคืออะไร? กลับไปที่รหัสเริ่มต้น:
999999999999999999 == 100000000000000000; // จริง
เห็นได้ชัดว่า 16 9s นั้นน้อยกว่า 308 10 วินาที ปัญหานี้ไม่มีส่วนเกี่ยวข้องกับ max_value และจะต้องมีการประกอบกับ Mantis M ด้วยตัวเลขเพียง 52 หลัก
สามารถอธิบายได้ในรหัส:
var x = 1; // เพื่อลดจำนวนการคำนวณค่าเริ่มต้นสามารถตั้งค่าให้มีขนาดใหญ่ขึ้นเช่น Math.pow (2, 53) - 10
ในขณะที่ (x! = x+1) x ++;
// x = 9007199254740992 I.E. 2^53
กล่าวคือเมื่อ X น้อยกว่าหรือเท่ากับ 2^53 จะทำให้มั่นใจได้ว่าความถูกต้องของ X จะไม่หายไป เมื่อ x มากกว่า 2^53 ความแม่นยำของ x อาจหายไป ตัวอย่างเช่น:
เมื่อ x คือ 2^53 + 1 การเป็นตัวแทนไบนารีของมันคือ:
10000000000 ... 001 (มี 52 0s อยู่ตรงกลาง)
เมื่อเก็บด้วยหมายเลขจุดลอยตัวสองจุด:
e = 1; M = 10,000..00 (รวม 52 0S, 1 เป็นบิตซ่อนอยู่)
เห็นได้ชัดว่านี่เป็นที่เก็บข้อมูล 2^53
ตามแนวคิดข้างต้นมันสามารถอธิบายได้ว่าสำหรับ 2^53 + 2 ไบนารีของมันคือ 100000 … 0010 (51 0s อยู่ตรงกลาง) และสามารถเก็บไว้ได้อย่างถูกต้อง
กฎ: เมื่อ x มากกว่า 2^53 และจำนวนตัวเลขที่สำคัญไบนารีมากกว่า 53 บิตจะมีการสูญเสียความแม่นยำ นี่คือหลักเช่นเดียวกับการสูญเสียความแม่นยำของทศนิยม
Hidden Bit สามารถใช้สำหรับการอ้างอิง: บทช่วยสอนเกี่ยวกับประเภท Java Double
สรุป
ความถูกต้องของทศนิยมและจำนวนเต็มขนาดใหญ่ไม่เพียง แต่หายไปในจาวาสคริปต์ การพูดอย่างเคร่งครัดภาษาการเขียนโปรแกรมใด ๆ (c/c ++/c#/java ฯลฯ ) ที่ใช้รูปแบบหมายเลขจุดลอยตัว IEEE 754 เพื่อจัดเก็บประเภทจุดลอยตัวมีปัญหาการสูญเสียความแม่นยำ ใน C# และ Java คลาสการห่อหุ้มทศนิยมและ bigdecimal มีให้เพื่อดำเนินการประมวลผลที่สอดคล้องกันเพื่อหลีกเลี่ยงการสูญเสียความแม่นยำ
หมายเหตุ: มีข้อเสนอทศนิยมในข้อกำหนด ECMASCript แล้ว แต่ยังไม่ได้รับการรับรองอย่างเป็นทางการ
สุดท้ายทดสอบทุกคน:
number.max_value + 1 == numer.max_value;
number.max_value + 2 == numer.max_value;
-
number.max_value + x == numer.max_value;
number.max_value + x + 1 == อินฟินิตี้;
-
number.max_value + number.max_value == Infinity;
// คำถาม:
// 1. ค่าของ x คืออะไร?
// 2. Infinity - number.max_value == x + 1; เป็นจริงหรือเท็จ?
การอภิปรายสั้น ๆ ข้างต้นเกี่ยวกับการสูญเสียความถูกต้องของทศนิยมและจำนวนเต็มขนาดใหญ่ในจาวาสคริปต์เป็นเนื้อหาทั้งหมดที่ฉันแบ่งปันกับคุณ ฉันหวังว่าคุณจะให้ข้อมูลอ้างอิงและฉันหวังว่าคุณจะสนับสนุน wulin.com มากขึ้น