การคูณและการหารเลข Floating-Point
เรียนจบบทนี้แล้วจะ
- อธิบายได้ว่าทำไมคูณ/หารไม่ต้องปรับ Exponent ให้เท่ากันแต่ต้องแก้ Bias
- หา Sign ด้วย XOR และคำนวณ Biased Exponent ของผลคูณและผลหาร
- คูณ Significand แบบ shift-and-add และหารแบบหารยาวฐานสอง
- Normalize และปัดเศษด้วย Guard, Round, Sticky
- รู้จัก Overflow, Underflow, การหารด้วยศูนย์ และ NaN
หัวข้อในบทนี้
ต่างจากการบวก/ลบอย่างไร
คูณเลขแบบ scientific notation ง่ายกว่าบวก: (2 × 10³) × (3 × 10⁴) = 6 × 10⁷ คือคูณตัวเลขสำคัญ แล้ว บวก เลขชี้กำลัง ไม่ต้องปรับเลขชี้กำลังให้เท่ากันก่อน ส่วนการหารก็หารตัวเลขสำคัญแล้ว ลบ เลขชี้กำลัง
| บวก/ลบ (บทที่ 4) | คูณ/หาร (บทที่ 5) | |
|---|---|---|
| ปรับ Exponent ให้เท่ากัน | ต้องทำ (เลื่อนตัวที่ Exponent น้อยกว่า) | ไม่ต้องทำ |
| Exponent ของผล | ใช้ตัวที่มากกว่า | EA + EB − 127 หรือ EA − EB + 127 |
| Sign | ขึ้นกับขนาดและการดำเนินการ | SA XOR SB |
| Significand | บวกหรือลบ | คูณหรือหาร |
| Normalize | ขวา 1 บิต (มีตัวทด) หรือซ้ายหลายบิต (ลบกันแล้วค่าหาย) | คูณ: ขวาไม่เกิน 1 บิต · หาร: ซ้ายไม่เกิน 1 บิต |
| ปัดเศษ | Guard, Round, Sticky | Guard, Round, Sticky (หาร: Sticky ได้จากเศษด้วย) |
การคูณ 8 ขั้น
- แยกทั้งสองค่าเป็น Sign, Biased exponent และ Significand (1.Fraction)
- Sign: S = SA XOR SB
- Exponent: E = EA + EB − 127
- คูณ Significand 24 บิตสองค่าแบบ shift-and-add ผลแม่นยำยาวได้ถึง 48 บิต และอยู่ในช่วง [1, 4)
- Normalize: ถ้าผลคูณ ≥ 2 เลื่อนขวา 1 บิตแล้ว Exponent + 1
- ปัดให้เหลือ 24 บิตด้วย Guard, Round, Sticky ถ้าปัดแล้วมีตัวทดให้ Normalize อีกครั้ง
- ตรวจช่วง: Overflow ได้ ±∞ · Underflow ได้ Subnormal หรือ ±0
- encode ผลเป็น 32 บิต
ทำไมลบ Bias หนึ่งครั้ง
Biased exponent แต่ละตัวมี +127 อยู่แล้ว: EA = eA + 127 และ EB = eB + 127 ถ้าบวกกันตรง ๆ จะได้ eA + eB + 254 ซึ่งมี Bias ซ้ำสองครั้ง จึงต้องลบ 127 ออกหนึ่งครั้งให้เหลือ (eA + eB) + 127
การคูณแบบ shift-and-add: ทุกบิต 1 ของตัวคูณ () ให้ หนึ่งแถว คือตัวตั้งเลื่อนไปตามตำแหน่งของบิตนั้น แล้วบวกทุกแถว
ตัวอย่าง M3 แบบละเอียด: 6.5 × 2.75
- 6.5 = 0 10000001 10100000000000000000000 → Significand 1.101, Biased exponent 129
2.75 = 0 10000000 01100000000000000000000 → Significand 1.011, Biased exponent 128 - Sign: 0 XOR 0 = 0
- Exponent: 129 + 128 − 127 = 130
- Significand: ตัวคูณ 1.011 มีบิต 1 อยู่ 3 ตัว จึงมี partial product 3 แถว
1.101000 + 0.011010 + 0.001101 = 10.001111
- Normalize: ผล ≥ 2 เลื่อนขวา 1 บิต Exponent 130 + 1 = 131
- ปัดเศษ: G = 0, R = 0, S = 0 → ไม่ต้องปัด
- ผลลัพธ์: 0 10000011 00011110000000000000000 = 0x418F0000 = 17.875
การหาร 8 ขั้น
- แยกทั้งสองค่าเป็น Sign, Biased exponent และ Significand
- Sign: S = SA XOR SB
- Exponent: E = EA − EB + 127
- หาร Significand ของตัวตั้งด้วยตัวหารด้วยการหารยาวฐานสอง ผลหารอยู่ในช่วง (0.5, 2) หาบิตให้พอสำหรับ Guard และ Round ส่วน Sticky เป็น 1 เมื่อเศษสุดท้ายไม่เป็นศูนย์
- Normalize: ถ้าผลหาร < 1 เลื่อนซ้าย 1 บิตแล้ว Exponent − 1
- ปัดเศษ แล้ว Normalize อีกครั้งถ้ามีตัวทด
- ตรวจช่วง (Overflow หรือ Underflow)
- encode ผลเป็น 32 บิต
ทำไมบวก Bias กลับหนึ่งครั้ง
EA − EB = (eA + 127) − (eB + 127) = eA − eB · Bias หักล้างกันหายไปหมด จึงต้องบวก 127 กลับหนึ่งครั้ง
การหารยาวฐานสองง่ายกว่าฐานสิบ เพราะแต่ละรอบมีแค่ 2 ทาง: ถ้าเศษ ≥ ให้ลบแล้วได้บิตผลหาร 1 ไม่งั้นได้ 0 แล้วเลื่อนเศษไปทางซ้าย 1 บิต
ตัวอย่าง D3 แบบละเอียด: 1 ÷ 3
- 1 = 0 01111111 00000000000000000000000 → Significand 1.0, Biased exponent 127
3 = 0 10000000 10000000000000000000000 → Significand 1.1, Biased exponent 128 - Sign: 0 XOR 0 = 0
- Exponent: 127 − 128 + 127 = 126
- Significand: 1.0 ÷ 1.1 ด้วยการหารยาว 27 รอบ ได้ผลหาร 0.10101010101010101010101010 เศษสุดท้าย ≠ 0 (ไม่มีวันเป็น 0)
- Normalize: ผล < 1 เลื่อนซ้าย 1 บิต Exponent 126 − 1 = 125
- ปัดเศษ: G = 1, R = 0, S = 1 (S มาจากเศษที่ไม่เป็นศูนย์) → ปัดขึ้น
- ผลลัพธ์: 0 01111101 01010101010101010101011 = 0x3EAAAAAB = 0.33333334 (ค่าที่เก็บจริง 0.3333333432674407958984375)
กรณีพิเศษและ IEEE exception
กรณีเหล่านี้ไม่ต้องไล่ทุกขั้น ใช้กฎสั้น ๆ ได้ผลทันที และ IEEE 754 ไม่ได้หยุดโปรแกรม แต่ให้ค่าพิเศษพร้อมแจ้ง exception
| กรณี | ผลลัพธ์ | Exception |
|---|---|---|
| x ÷ 0 (x เป็นค่าจำกัดที่ไม่ใช่ 0) | ±∞ (Sign = XOR) | Division by zero |
| 0 ÷ 0, ∞ ÷ ∞, 0 × ∞ | NaN | Invalid operation |
| 0 × x, 0 ÷ x (x เป็นค่าจำกัดที่ไม่ใช่ 0) | ±0 (Sign = XOR) | — |
| x ÷ ∞ (x เป็นค่าจำกัด) | ±0 | — |
| ∞ × x (x ไม่ใช่ 0), ∞ ÷ x (x เป็นค่าจำกัด) | ±∞ | — |
| มีตัวใดตัวหนึ่งเป็น NaN | NaN | — |
| ผลใหญ่กว่าค่าจำกัดที่มากที่สุด | ±∞ | Overflow (และ Inexact) |
| ผลเล็กกว่าค่า normalized ที่เล็กที่สุด | Subnormal หรือ ±0 | Underflow (เมื่อปัดแล้วไม่ตรง) |
และ ดูได้ในขั้น “ตรวจ Overflow / Underflow” ของ Simulator ลองตัวอย่าง M5, M6, D4, D5 หรือปุ่มค่าพิเศษ
ข้อผิดพลาดที่เจอบ่อย
- ลืมลบ Bias ตอนคูณ (ได้ EA + EB เฉย ๆ) หรือลืมบวก Bias กลับตอนหาร → Exponent ผิดไป 127
- ลืม hidden bit คูณ/หารแค่ Fraction 23 บิตโดยไม่มีเลข 1 หน้าจุด → ผลผิดทั้งหมด
- ใส่ Sign ผิด คูณ/หารใช้ XOR เสมอ ไม่ได้ขึ้นกับว่าค่าไหนใหญ่กว่า
- Normalize ผิดทาง คูณเลื่อนขวา (ผลอาจถึง 2 ขึ้นไป) หารเลื่อนซ้าย (ผลอาจต่ำกว่า 1)
- ทิ้งเศษของการหาร เศษที่ไม่เป็น 0 ต้องกลายเป็น Sticky = 1 ไม่งั้นปัดผิดได้
แบบจำลองเพื่อการเรียนรู้
ขั้นตอนนี้เป็นแบบจำลองของขั้นหลักเพื่อการเรียนรู้ ไม่ใช่วงจรคูณหรือหารของฮาร์ดแวร์รุ่นใดรุ่นหนึ่ง (เช่น array multiplier, Booth encoding, SRT division) แต่ผลลัพธ์ตรงกับ IEEE 754 ทุกบิต
ตัวอย่าง M1–M6 และ D1–D5
Simulator: คูณและหาร
เลือก × หรือ ÷ แล้วดูทีละขั้น: XOR ของ Sign, การแก้ Bias (ฐานสิบและฐานสอง 8 บิต), partial product ทีละแถว, การหารยาวทีละแถว, Normalize, ปัดเศษ, ตรวจช่วง และผลลัพธ์ · ใส่ค่าพิเศษ 0, −0, ∞, −∞, NaN ได้จากปุ่มด้านล่างช่องกรอก
พิมพ์ทศนิยมหรือ scientific notation เช่น 0.1, -6.5, 1e-8 หรือ inf, nan · ใช้จุดเป็นจุดทศนิยม
ตัวอย่าง
- Sign
- Exponent
- Fraction
- Hidden bit
- Guard / Round / Sticky
1/9แยกส่วนของ A
ขั้นที่ 1 จาก 9: แยกส่วนของ A
ประเภท: Normal · ค่าที่เก็บจริง: 6.5
Sign = 0 · Biased exponent = 129 (Exponent จริง 2)
Significand = 1.101
ผลลัพธ์ 6.5 × 2.75
- ผลแม่นยำของค่าที่พิมพ์
- 17.875
- ผลแม่นยำของค่าที่เก็บจริงใน float
- 17.875
- ผล float (binary32)
- 17.8750x418F0000เก็บจริง: 17.875
ผลต่างจากค่าที่พิมพ์: 0 (ตรงพอดี)
สรุปท้ายบท
- Sign ของผลคูณและผลหาร = SA XOR SB เสมอ
- คูณ: E = EA + EB − 127 (ลบ Bias ที่ซ้ำ) · หาร: E = EA − EB + 127 (บวก Bias ที่หายไปกลับ)
- คูณ/หาร Significand ที่รวม hidden bit แล้ว: คูณแบบ shift-and-add (ผลใน [1, 4)) · หารแบบหารยาว (ผลใน (0.5, 2))
- Normalize: คูณเลื่อนขวาไม่เกิน 1 บิต · หารเลื่อนซ้ายไม่เกิน 1 บิต
- ปัดเศษด้วย Guard, Round, Sticky (round to nearest, ties to even) การหารเอาเศษที่ไม่เป็น 0 มาเป็น Sticky
- กรณีพิเศษ: x ÷ 0 = ±∞ · 0 ÷ 0, ∞ ÷ ∞, 0 × ∞ = NaN · ผลใหญ่เกินเป็น ±∞ (Overflow) · ผลเล็กเกินเป็น Subnormal หรือ ±0 (Underflow)