คำอธิบายปัญหา
When using Java to calculate floating point numbers in the project, it was found that for calculations like 4.015*100, the result was not the expected 401.5, but 401.499999999999999999999999999999994. ตัวเลขจำนวนมากดังกล่าวไม่เป็นมิตรสำหรับการแสดงผล
สาเหตุของปัญหา: การแสดงหมายเลขจุดลอยตัว
หลังจากปรึกษาข้อมูลที่เกี่ยวข้องฉันพบว่าเหตุผลก็คือหมายเลขจุดลอยตัวในคอมพิวเตอร์ไม่สามารถแสดงออกได้อย่างถูกต้องอย่างถูกต้อง ตัวอย่างเช่นสำหรับสองประเภท 38414.4 คอมพิวเตอร์เก็บไว้เช่นนี้:
แปลงเป็นไบนารี: 100101100001110.01100110011001100110011001100110011001100
เปลี่ยนเป็นเรื่อง
เรียนรู้วิธีการนับ: 1.0010110000111001100110011001100110011001100110011001100110011001100 × 2^15
รูปแบบการเข้ารหัสสองครั้งมีดังนี้:
บิตลงนามสองครั้ง 1 หลักรหัสสเตจ 11 หลัก Mantissa 52 หลัก
บิตสัญลักษณ์: จำนวนบวกคือ 0
รหัสคำสั่งซื้อ: 15 เป็นจำนวนบวกดังนั้นบิตสูงสุดคือ 1 บิตต่ำสุดคือลบ 1 ซึ่งคือ 10,000001110
Mantissue: ลบค่าเริ่มต้น 1 ของบิตสูงสุดซึ่งคือ 00101100001110010011001100110011001100110011001100100
รวมการเข้ารหัสสุดท้ายคือ: 0 1000001110 00101100001110011001100110011001100110011001100110011001100100
จากที่นี่เราจะเห็นได้ว่าเหตุผลหลักคือการเข้ารหัสแบบไบนารีทำให้ส่วนที่เป็นเศษส่วนเป็นไปไม่ได้ที่จะแสดงออกอย่างสมบูรณ์เช่น 0.4 = 0.25 + 0.125 + ... ซึ่งสามารถปิดได้อย่างไม่สิ้นสุด ดังนั้นข้อผิดพลาดความแม่นยำจะเกิดขึ้นเมื่อคำนวณหมายเลขจุดลอยตัว
วิธีแก้ปัญหา: ความแม่นยำสูง
BigDecimal ใน Java สามารถรองรับการดำเนินการหมายเลขจุดลอยตัวด้วยความแม่นยำโดยพลการ ในหนังสือ "Java ที่มีประสิทธิภาพ" ขอแนะนำว่า Float และ Double ใช้สำหรับการคำนวณทางวิทยาศาสตร์หรือการคำนวณทางวิศวกรรมในขณะที่ Java.math.Bigdecimal ใช้ในการคำนวณเชิงพาณิชย์
มีวิธีการก่อสร้างมากมายสำหรับ bigdecimal เช่น bigdecimal (double) และ bigdecimal (สตริง) ควรสังเกตว่าพารามิเตอร์การก่อสร้างเป็นประเภทสตริงเพื่อให้แน่ใจว่าความแม่นยำจะไม่หายไปเนื่องจากประเภทสองเท่านั้นไม่สมบูรณ์ ดังนั้นจึงจำเป็นต้องเขียนดังนี้: bigdecimal ("0.02")
การดำเนินการขั้นพื้นฐานของสองประเภทสามารถพบได้ใน bigdecimal นอกจากนี้ BigDecimal ยังสามารถใช้สำหรับการจัดรูปแบบเอาต์พุตด้วย numberFormat
BigDecimal จะสร้างวัตถุ bigdecimal ใหม่เมื่อทำการดำเนินการดังนั้นมันจะนำค่าใช้จ่ายประสิทธิภาพมากขึ้นเมื่อเทียบกับสองเท่า
การศึกษาเบื้องต้นเกี่ยวกับการใช้งานที่มีความแม่นยำสูง
ดังนั้น BigDecimal จะสามารถแสดงถึงความแม่นยำโดยพลการได้อย่างไร? นี่เป็นเพียงการวิเคราะห์เบื้องต้น
ก่อนอื่นมาดูการใช้งาน BigInteger ประเภท int ธรรมดาคือ 32 บิตดังนั้นจึงมีข้อ จำกัด ช่วง BigInteger มีตัวแปรสมาชิก Int [] MAG เพื่อให้อาร์เรย์ int ที่ยาวขึ้นทำให้เป็นไปได้ที่จะเป็นตัวแทนจำนวนเต็มทุกขนาด
มาดูการใช้งาน BigDecimal การแนะนำอย่างเป็นทางการของมันบอกว่า bigdecimal ใด ๆ สามารถแสดงเป็น unscaledvalue × 10^-cale UNSCALEDVALUE เป็นจำนวนเต็มทุกขนาดซึ่งสอดคล้องกับตัวแปรสมาชิก BigInteger intval ในซอร์สโค้ด สเกลคือคำสั่งที่สอดคล้องกับสเกล Int ตัวแปรในซอร์สโค้ด ด้วยวิธีนี้ BigDecimal ถูกนำไปใช้ตาม BigInteger
ด้านบนเป็นวิธีแก้ปัญหาความแม่นยำของจุดลอยตัวใน Java ที่แนะนำโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉัน