การบวกและการลบเลข Floating-Point
เรียนจบบทนี้แล้วจะ
- เข้าใจว่าทำไมบวกเลข Floating-Point ตรง ๆ ไม่ได้
- อธิบายขั้นตอนหลักของการบวกและลบได้
- ติดตามขั้นตอนระหว่างการคำนวณได้
- เห็นว่าการปัดเศษทำให้ผลต่างจากคำตอบแม่นยำได้อย่างไร
หัวข้อในบทนี้
ทำไมบวกเลข Floating-Point ตรง ๆ ไม่ได้
ลองบวกเลขฐานสิบแบบ scientific notation: 1.5 × 10³ + 2.0 × 10¹ จะเอา 1.5 + 2.0 ไม่ได้ เพราะเลขชี้กำลังไม่เท่ากัน ต้องปรับให้เป็น ×10³ ก่อน:
Floating-Point ก็เหมือนกัน ต้องทำให้ Exponent เท่ากันก่อน โดยเลื่อน Significand ของตัวที่ Exponent น้อยกว่าไปทางขวา แล้วจึงบวกหรือลบ Significand
7 ขั้นของการบวก/ลบ Floating-Point
- encode ตัวเลขทั้งสองเป็น IEEE 754 (ใช้ค่าที่เก็บจริง ไม่ใช่ค่าที่พิมพ์)
- แยก Sign, Exponent และ (เติม hidden bit)
- เทียบ Exponent แล้วเลื่อน Significand ของตัวที่ Exponent น้อยกว่าไปทางขวาจนเท่ากัน
- บวก Significand ถ้า Sign เหมือนกัน หรือลบถ้า Sign ต่างกัน (การลบ A − B คือ A + (−B) จึงกลับ Sign ของ B ก่อน)
- Normalize: ถ้าผล ≥ 2 เลื่อนขวา 1 บิตแล้ว Exponent + 1 · ถ้าบิตหน้าเป็น 0 (ลบกันแล้วเหลือน้อย) เลื่อนซ้ายจนได้ 1.xxx
- ปัดเศษให้เหลือ 24 บิตด้วย Guard, Round, Sticky
- encode ผลลัพธ์เป็น 32 บิต
แบบจำลองเพื่อการเรียนรู้
ขั้นตอนในบทนี้เป็นแบบจำลองของขั้นหลักเพื่อให้เข้าใจหลักการ ไม่ใช่รายละเอียดของวงจรบวกเลขในฮาร์ดแวร์ทุกแบบ แต่ผลลัพธ์ทุกบิตตรงกับมาตรฐาน IEEE 754
Guard, Round และ Sticky
ตอนเลื่อน Significand ไปทางขวา บิตบางส่วนจะเลยตำแหน่งสุดท้ายที่เก็บได้ ถ้าทิ้งไปเลยจะปัดผิด ฮาร์ดแวร์จึงเก็บบิตเพิ่ม 3 ช่อง เรียกว่า :
- Guard (G): บิตแรกถัดจากบิตสุดท้ายที่เก็บได้ (มีค่าครึ่งหนึ่งของบิตสุดท้าย)
- Round (R): บิตถัดจาก Guard
- Sticky (S): OR ของบิตที่เหลือทั้งหมด ถ้ามีบิต 1 หลุดออกไปแม้แต่ตัวเดียว S = 1
3 บิตนี้พอสำหรับปัดเศษให้ได้ผลเหมือนคำนวณแบบแม่นยำแล้วปัดครั้งเดียว
Round to nearest, ties to even
IEEE 754 ปัดแบบ เป็นค่าเริ่มต้น ดูจาก G, R, S:
| G | R หรือ S | ความหมาย | ปัด |
|---|---|---|---|
| 0 | อะไรก็ได้ | ส่วนที่ทิ้งน้อยกว่าครึ่ง | ลง (ตัดทิ้ง) |
| 1 | มี 1 | ส่วนที่ทิ้งมากกว่าครึ่ง | ขึ้น (+1 ที่บิตสุดท้าย) |
| 1 | 0 ทั้งคู่ | กึ่งกลางพอดี (tie) | เลือกให้บิตสุดท้ายเป็น 0 (เลขคู่) |
ที่ต้องเลือกเลขคู่ตอนเสมอ เพราะถ้าปัดขึ้นทุกครั้ง ผลรวมของการคำนวณจำนวนมากจะเอียงไปทางมากขึ้นเรื่อย ๆ การเลือกเลขคู่ทำให้ปัดขึ้นกับปัดลงพอ ๆ กัน
ตัวอย่าง A1–A6
ตัวเลขทุกตัวด้านล่างคำนวณสดจาก engine (กดตัวอย่างใน Simulator ด้านล่างเพื่อดูทีละขั้น) ตัวอย่างที่น่าสนใจที่สุดคือ A2: 0.1 + 0.2 ไม่ได้ 0.3 พอดี ผลแม่นยำของค่าที่เก็บจริงคือ 0.300000004470348358154296875 แล้วถูกปัดเป็น 0.3
Floating-Point Simulator: บวกและลบ
พิมพ์ตัวเลข 2 ค่าแล้วเลือก + หรือ − เครื่องมือจะเก็บทั้งสองค่าเป็น Single Precision ก่อน แล้วคำนวณตามกฎของ Floating-Point ทีละขั้น (ไม่ได้คำนวณคำตอบแม่นยำแล้วค่อยแปลง) กดกล่องใน flowchart เพื่อกระโดดไปขั้นนั้นได้ ในขั้นเลื่อนให้ Exponent เท่ากัน ลองกด “เลื่อนทีละบิต”
พิมพ์ทศนิยมหรือ scientific notation เช่น 0.1, -6.5, 1e-8 หรือ inf, nan · ใช้จุดเป็นจุดทศนิยม
ตัวอย่าง
เก็บได้ไม่พอดี ค่าที่เก็บจริงคือ 0.10000000149011611938…
เก็บได้ไม่พอดี ค่าที่เก็บจริงคือ 0.20000000298023223876…
- Sign
- Exponent
- Fraction
- Hidden bit
- Guard / Round / Sticky
1/8แยกส่วนของ A
ขั้นที่ 1 จาก 8: แยกส่วนของ A
ประเภท: Normal · ค่าที่เก็บจริง: 0.100000001490116119384765625
Sign = 0 · Biased exponent = 123 (Exponent จริง −4)
Significand = 1.10011001100110011001101
ผลลัพธ์ 0.1 + 0.2
- ผลแม่นยำของค่าที่พิมพ์
- 0.3
- ผลแม่นยำของค่าที่เก็บจริงใน float
- 0.300000004470348358154296875
- ผล float (binary32)
- 0.30x3E99999Aเก็บจริง: 0.300000011920928955078125
ผลต่างจากค่าที่พิมพ์: 1.1920928955… × 10−8
- Inexact (ปัดเศษแล้วไม่ตรง)
สรุปท้ายบท
- ต้องปรับ Exponent ให้เท่ากันก่อน โดยเลื่อน Significand ของตัวที่ Exponent น้อยกว่าไปทางขวา
- Sign เหมือนกันให้บวก Significand ต่างกันให้ลบ ผลใช้ Sign ของตัวที่มีขนาดใหญ่กว่า (A − B = A + (−B))
- Normalize: ผล ≥ 2 เลื่อนขวา 1 บิต · ลบกันจนบิตหน้าหาย เลื่อนซ้ายได้หลายบิต แล้วปรับ Exponent ตาม
- ปัดเศษด้วย Guard, Round, Sticky แบบ round to nearest, ties to even
- ความแม่นยำจำกัดทำให้ผลต่างจากคำตอบแม่นยำได้ เช่น 0.1 + 0.2 ไม่ได้ 0.3 พอดี และ 1 + 1e-8 ได้ 1 (ตัวเล็กหายไปทั้งตัว)