การแทนค่า Floating-Point ตามมาตรฐาน IEEE 754
เรียนจบบทนี้แล้วจะ
- อธิบายว่าทำไมต้องใช้ Floating-Point
- เชื่อม scientific notation ฐานสิบกับรูป 1.xxx × 2ᵉ
- ระบุ field Sign, Exponent, Fraction ของ Single Precision ได้
- encode เลขฐานสิบเป็น 32 บิตและ decode กลับได้
หัวข้อในบทนี้
Scientific notation: จากฐานสิบสู่ฐานสอง
นักวิทยาศาสตร์เขียนเลขที่ใหญ่หรือเล็กมากในรูป ตัวเลข 1 หลักหน้าจุด × 10 ยกกำลัง เช่น
ฐานสองทำแบบเดียวกันได้ เลื่อนจุดจนหน้าจุดเหลือเลข 1 ตัวเดียว (ในฐานสองหลักแรกที่ไม่ใช่ 0 ต้องเป็น 1 เสมอ) แล้วจดว่าเลื่อนไปกี่ตำแหน่ง ขั้นนี้เรียกว่า
ทำไมต้องมี Floating-Point
ถ้าเก็บจำนวนจริงแบบกำหนดตำแหน่งจุดตายตัว (fixed-point) เช่น 16 บิตหน้าจุด 16 บิตหลังจุด จะเก็บได้แค่ค่าในช่วงแคบ ๆ ค่าใหญ่มาก (ระยะทางระหว่างดาว) หรือเล็กมาก (ขนาดอะตอม) จะเก็บไม่ได้เลย
เก็บแค่ ตัวเลขสำคัญ กับ เลขชี้กำลัง แยกกัน จุดจึง “ลอย” ไปอยู่ตรงไหนก็ได้ตามขนาดของค่า ด้วย 32 บิตเท่าเดิมเก็บได้ตั้งแต่ประมาณ 1 × 10−45 ถึง 3.4028235 × 1038 แลกกับความละเอียดที่มีจำกัด (ราว 7 หลักฐานสิบ)
มาตรฐานที่ทุกเครื่องใช้คือ IEEE 754 ซึ่งกำหนดรูปแบบบิต วิธีปัดเศษ และค่าพิเศษ ทำให้ทุกเครื่องคำนวณได้ผลเหมือนกันทุกบิต
Single Precision: Sign 1 · Exponent 8 · Fraction 23
ใช้ 32 บิต แบ่งเป็น 3 ส่วน (สีของแต่ละส่วนเหมือนกันทุกบทในเว็บนี้):
- Sign
- Exponent
- Fraction
- (1 บิต): 0 = บวก, 1 = ลบ
- (8 บิต): เลขชี้กำลังของ 2 เก็บแบบบวก Bias
- (23 บิต): บิตหลังจุดของตัวเลขสำคัญ
แตะที่บิตใดก็ได้เพื่อดูว่าบิตนั้นอยู่ส่วนไหนและมีน้ำหนักเท่าไร
Exponent และ Bias 127
เลขชี้กำลังจริงมีทั้งบวกและลบ (ค่าเล็กกว่า 1 มีเลขชี้กำลังติดลบ) แต่ field Exponent เก็บเป็นจำนวนเต็มไม่มีเครื่องหมาย 0–255 จึงบวก 127 ก่อนเก็บ ค่าที่เก็บเรียกว่า
เช่น exponent จริง 2 เก็บเป็น 2 + 127 = 129 = 10000001 · exponent −3 เก็บเป็น −3 + 127 = 124 = 01111100
ทำไมใช้ Bias แทนบิตเครื่องหมาย
เพราะเมื่อเก็บแบบบวก Bias ค่าบวกที่ใหญ่กว่าจะมีรูปแบบบิตที่เป็นจำนวนเต็มใหญ่กว่าเสมอ ฮาร์ดแวร์จึงเทียบขนาดของ float ได้ด้วยวงจรเทียบจำนวนเต็มธรรมดา
ขั้นตอน encode: ฐานสิบ → 32 บิต
- แปลงเป็นฐานสอง (ส่วนเต็มหาร 2 ซ้ำ ส่วนเศษคูณ 2 ซ้ำ)
- Normalize ให้อยู่ในรูป 1.xxx × 2ᵉ
- กำหนด Sign bit
- คำนวณ Biased exponent = e + 127 แล้วเขียนเป็นฐานสอง 8 บิต
- เติม Fraction ด้วยบิตหลังจุด 23 บิต (ถ้ามีบิตเกินต้องปัดเศษ)
- รวม 3 ส่วนเป็น 32 บิต (มักเขียนเป็น hex 8 หลัก)
- แปลงเป็นฐานสอง: 5.75 = 101.112
- Normalize: 1.0111 × 22 (เลื่อนจุดไปทางซ้าย 2 ตำแหน่ง)
- Sign: ค่าบวก → 0
- Biased exponent: 2 + 127 = 129 = 10000001
- Fraction (บิตหลังจุด เติม 0 จนครบ 23 บิต): 01110000000000000000000
- รวม: 0 10000001 01110000000000000000000 = 0x40B80000
- แปลงเป็นฐานสอง: 13.25 = 1101.012
- Normalize: 1.10101 × 23 (เลื่อนจุดไปทางซ้าย 3 ตำแหน่ง)
- Sign: ค่าลบ → 1
- Biased exponent: 3 + 127 = 130 = 10000010
- Fraction (บิตหลังจุด เติม 0 จนครบ 23 บิต): 10101000000000000000000
- รวม: 1 10000010 10101000000000000000000 = 0xC1540000
ขั้นตอน decode: 32 บิต → ฐานสิบ
ทำย้อนกลับ: แยก field → ดูประเภทจาก Exponent → exponent จริง = E − 127 → เติม hidden bit → แทนค่าในสูตร
- แยก field: Sign 0 · Exponent 01111100 = 124 · Fraction 01000000000000000000000
- Exponent 1–254 จึงเป็นค่า Normal · Exponent จริง = 124 − 127 = -3
- Significand = hidden bit 1 + Fraction = 1.01
- ค่า = (−1)0 × 1.01 × 2−3 = 0.15625
0.1 เก็บไม่พอดี
จากบทที่ 2 เรารู้แล้วว่า 0.1 ในฐานสองซ้ำไม่รู้จบ Single Precision มี Significand แค่ 24 บิต จึงต้องปัดให้ใกล้ที่สุด (round to nearest, ties to even) ผลคือ 0x3DCCCCCD ซึ่งมีค่าจริงเท่ากับ
ต่างจาก 0.1 อยู่ 1.4901161… × 10−9 เล็กมากแต่ไม่ใช่ 0 เวลาโปรแกรมแสดงผลเป็น 0.1 เพราะเลือกทศนิยมที่สั้นที่สุดที่แปลงกลับได้บิตเดิม
จำนวนจริงส่วนใหญ่เก็บแบบพอดีไม่ได้ด้วยบิตจำนวนจำกัด ค่าที่เก็บเป็นแค่ค่าที่ใกล้ที่สุดที่ format นั้นมี
Exponent 0 และ 255 มีความหมายพิเศษ
| Exponent | Fraction | ความหมาย |
|---|---|---|
| 0 | 0 | ±0 (มีทั้ง +0 และ −0) |
| 0 | ≠ 0 | : 0.Fraction × 2−126 (ไม่มี hidden bit) |
| 1–254 | อะไรก็ได้ | ค่าปกติ (normal) ตามสูตรข้างบน |
| 255 | 0 | ± |
| 255 | ≠ 0 |
- ค่าจำกัดที่มากที่สุด: 0x7F7FFFFF ≈ 3.4028235 × 1038
- ค่า normal ที่เล็กที่สุด: 0x00800000 = 2−126 ≈ 1.1754944 × 10−38
- ค่า subnormal ที่เล็กที่สุด: 0x00000001 = 2−149 ≈ 1 × 10−45
บทนี้แสดงค่าพิเศษแบบสั้น ๆ ส่วนการคำนวณกับค่าพิเศษอธิบายเต็มในบทที่ 5
IEEE 754 Converter
มี 2 โหมด: พิมพ์เลขฐานสิบเพื่อ encode เป็น 32 บิต หรือกด/พิมพ์บิตเพื่อ decode กลับเป็นฐานสิบ แต่ละโหมดดูได้ทีละขั้นด้วยปุ่มด้านล่าง ในขั้น Normalize ลองกดปุ่มเลื่อนจุดเองก่อนดูคำตอบ
เก็บได้พอดี ไม่ต้องปัดเศษ
- Sign
- Exponent
- Fraction
- Hidden bit
- Guard / Round / Sticky
1/7ส่วนจำนวนเต็มเป็นฐานสอง
ขั้นที่ 1 จาก 7: ส่วนจำนวนเต็มเป็นฐานสอง
| หารด้วย 2 | ได้ | เศษ (บิต) |
|---|---|---|
| 5 ÷ 2 | 2 | 1 |
| 2 ÷ 2 | 1 | 0 |
| 1 ÷ 2 | 0 | 1 |
อ่านเศษจากล่างขึ้นบน: 1012
หารด้วย 2 ไปเรื่อย ๆ จนได้ 0 เศษของแต่ละรอบคือบิต เริ่มจากบิตขวาสุด (น้ำหนัก 2⁰)
ข้อจำกัด: เครื่องมือนี้ใช้ Single Precision (32 บิต) และปัดแบบ round to nearest, ties to even เท่านั้น · รับทศนิยม, scientific notation (เช่น 1.5e-7), inf และ nan · ไม่เกิน 200 ตัวอักษร
สรุปท้ายบท
- Scientific notation เขียนค่าเป็นตัวเลขสำคัญ × ฐานยกกำลัง ฐานสองเขียนเป็น 1.xxx × 2ᵉ (Normalize)
- Single Precision มี 32 บิต: Sign 1 บิต · Exponent 8 บิต · Fraction 23 บิต
- Exponent เก็บแบบบวก Bias 127 (Biased exponent = e + 127) และค่า normal มี hidden bit = 1 ที่ไม่ต้องเก็บ
- encode: ฐานสอง → Normalize → Sign → e + 127 → Fraction 23 บิต (ปัดถ้าเกิน) → รวม 32 บิต · decode ทำย้อนกลับ
- Exponent 0 ใช้กับ ±0 และ Subnormal · Exponent 255 ใช้กับ ±∞ และ NaN
- ด้วยบิตจำกัด ค่าส่วนใหญ่ (เช่น 0.1) เก็บได้แค่ค่าที่ใกล้ที่สุด จึงมีความคลาดเคลื่อนเล็ก ๆ เสมอ