可表示數值、精度與運算準確度 (Representable Numbers, Precision and Accuracy)

重點總覽 (Overview)

主題 核心觀念 由什麼決定
Representable numbers 可被該格式「精確表示」的數值集合;落在區間內的值會被 round 到最近的代表數 exponent 定區間、mantissa 定每區間密度
Precision (精度) 把數值表示成代表數時的「最大表示誤差」 mantissa 位數(多 1 bit → 誤差減半)
Accuracy (準確度) 一個運算所引入的「最大運算誤差」 執行的運算(rounding、近似演算法)
Gap near 0 normalized 1.M 無法表示接近 0 的數,造成 0 附近巨大空洞 normalized mantissa 排除了 0
Denormalization E=0 時改用 0.M、固定 exponent,填平 0 附近空洞(IEEE 採用) 硬體成本高,早期 CUDA 不支援
Special patterns E 全 1 → ±∞(M=0) 或 NaN(M≠0);E 全 0 → denorm 或 0 保留 bit pattern
ULP / rounding 正確捨入的 add/sub 誤差 ≤ 0.5 ULP;除法/超越函數可能更大 結果 mantissa 位數超出格式
Important

Precision ≠ Accuracy:precision 是「靜態」的表示能力(看 mantissa 位數),accuracy 是「動態」的運算引入誤差(看運算如何實作)。考試最常混淆這兩者。


可表示數值 (Representable Numbers)

Representable numbers = 該格式能精確表示的數值;任何落在兩代表數之間的值都必須被 round 到其中一個。範例採用 5-bit 格式:1-bit S2-bit E(excess-1)、2-bit M(省略 "1.")。

正的代表數(每區間 4 個 = 2^N,N=mantissa 位數):

E (code) actual exp 區間 代表數 (spacing)
00 2^-1 [0.5, 1.0) 0.5, 0.625, 0.75, 0.875 (2^-3)
01 2^0 [1.0, 2.0) 1.0, 1.25, 1.5, 1.75 (2^-2)
10 2^1 [2.0, 4.0) 2.0, 2.5, 3.0, 3.5 (2^-1)
11 reserved (∞ / NaN,見下)

五個觀察 (five observations):

  1. Exponent bits 定義主要區間 (major intervals) — 區間落在相鄰 2 的次方之間。
  2. Mantissa bits 定義每區間的代表數個數 — N 個 mantissa bit → 每區間 2^N 個代表數 → 決定 precision
  3. 0 不可表示 — no-zero 格式中 0 缺席(嚴重缺陷)。
  4. 越靠近 0、代表數越密 — 每往 0 移動一個區間,寬度減半(4 → 2 → 1 → 0.5…);絕對值小的數被表示得更精確(區間寬度 = 該區間最大 rounding error)。
  5. 但「緊鄰 0」處反而出現空洞 — normalized 1.M 排除 0,造成 0 附近最大區間無代表數。
no-zero 格式(正半線;0 不可表示):
   0  [====== gap ======]  *  *  *  *  | *   *   *   *  | *    *    *    *
   ^                        0.5 .625 .875  1.0 1.25  1.75  2.0  2.5  3.0  3.5
   |                        \__ 4 個/區間, spacing 2^-3 _/ \_ spacing 2^-2 _/
   0 缺席 + 0..0.5 空洞
Warning

觀察 4(越靠近 0 越精確)在「緊鄰 0」處失效(觀察 5)。一般而言,m-bit mantissa 在最接近 0 的區間會比次一區間多引入 2^m 倍 誤差;若演算法用這些小數當分母,誤差會在除法中被放大 → numerical instability。


反正規化 vs 突然下溢 (Denormalization vs Abrupt Underflow)

兩種把 0 納入 並處理 0 附近空洞的方法,IEEE 最終採用 denormalization

方法 E=0 時的解讀 對 0 附近的效果 採用情形
No-zero 無(沿用 1.M) 0 不可表示 + 巨大空洞 不可用
Abrupt underflow 一律當作 0 0 可表示,但空洞更大(0→1.0 全變 0) 早期 minicomputer / 高速 ALU(圖簡單快)
Denormalization mantissa 改 0.M,exponent 固定 = 上一區間值 均勻填平空洞,最後兩區間 spacing 相同 IEEE 標準
abrupt UF :  0 [========== 更大 gap ==========]  | 1.0 1.25 1.5 1.75 | 2.0 ...   (E=00 全 → 0)
denormal  :  0   0.25   0.5   0.75               | 1.0 1.25 1.5 1.75 | 2.0 ...   (spacing 0.25, 均勻!)
              \__ 把最後一區間 4 個數攤開填滿空洞 _/

Denormalized 數值公式(n-bit exponent,當 E=0 時):

value = (-1)^S * 0.M * 2^(2 - 2^(n-1))
Tip

對 5-bit 範例(n=2):指數 = 2 − 2^1 = 0,故 00001 = 0.01_B × 2^0 = 2^-2 = 0.25。對 IEEE single(n=8):指數 = 2 − 128 = −126,denorm 值 = 0.M × 2^-126。

Warning

denormalization 公式複雜、硬體須偵測並切換表示法,高速實作成本極高(中等硬體常引入數千 clock cycle 延遲)。這是早期 CUDA 不支援它的原因。

  • compute capability 1.3+:支援 denormalized double-precision
  • compute capability 2.0+:支援 denormalized single-precision

精度小結:precision = 把數值表示成代表數的最大誤差;mantissa 每多 1 bit,最大誤差減半。這正是 IEEE double 相對 single 更精確的原因。


特殊位元樣式 (Special Bit Patterns: NaN / Infinity)

IEEE 用保留的 exponent 全 1 / 全 0 樣式表達特殊值(Fig. A.8):

exponent mantissa 意義
11…1 ≠ 0 NaN (Not a Number)
11…1 = 0 (-1)^S × ∞
00…0 ≠ 0 denormalized
00…0 = 0 0

IEEE 標準格式位元配置:

格式 S E M 備註
single 1 8 23
double 1 11 52 mantissa 多 29 bits → 最大表示誤差降為 single 的 1/2^29;exponent 多 3 bits → 動態範圍更廣

Infinity 與 NaN 的產生:

兩種 NaN:

類型 MSB of mantissa 行為 用途
Signaling NaN cleared (0) 作為運算輸入時觸發 exception 標記未初始化資料、mission-critical 中斷執行
Quiet NaN set (1) 運算後產生另一個 quiet NaN,不觸發 exception 結果印成 "NaN",使用者事後檢視
Warning

目前 GPU 硬體不支援 signaling NaN(大規模平行執行中難以精確 signaling)。


運算準確度與捨入 (Arithmetic Accuracy and Rounding)

Accuracy = 一個運算引入的最大誤差(與 precision 區分:precision 看 mantissa 位數,accuracy 看「運算如何實作」)。最常見誤差來源為 rounding:結果 mantissa 需要的位數超過格式容量。

Alignment shifting(對齊位移):兩 operand exponent 不同時,較小者的 mantissa 右移直到 exponent 相等,可能使結果位數變多。

範例(5-bit 格式):1.00_B × 2^1 + 1.00_B × 2^-2

  指數差 = 3 → 第二個數 mantissa 右移 3 位:
      1.00_B × 2^1
   +  0.001_B × 2^1      (= 1.00_B × 2^-2 對齊後)
   ----------------
   =  1.001_B × 2^1      <- 理想結果,需 3 個 mantissa bit (格式只有 2!)
   → round 到 1.01_B×2^1 或 1.00_B×2^1
   → 引入誤差 0.001_B × 2^1 = 最低位權的一半 = 0.5 ULP

ULP (Units in the Last Place) = mantissa 最低位的位權;它是衡量運算誤差的單位。

關鍵準確度規則:

運算 典型最大誤差 說明
add / subtract ≤ 0.5 ULP 正確捨入硬體;今日所有 CUDA device 的 add/sub 達此準確度
division / 超越函數 (sin, 1/x…) 可能 > 0.5 ULP 用 polynomial approximation 實作,項數不足則誤差變大

範例(inversion 用近似):理想 1.00_B × 2^1,硬體給 1.10_B × 2^1 → 誤差 = 0.10_B = 最低位權 0.01_B 的 2 倍 = 2 ULP(早期裝置確實有 2 ULP 的 inversion 誤差)。

Tip

越多 transistor(越新世代)→ special function unit 用更多近似項 → 算術運算更準確。但快速近似運算的準確度仍可能低於正確捨入,這是使用 hardware intrinsics(如 __sinf)必須權衡的點(見 17-Iterative-MRI-Reconstruction/04-Hardware-Trig-Accuracy-and-Performance-Tuning)。


考試/面試重點 (Exam / Test Patterns)

情境 / 關鍵字 答案 / 技巧
「precision 由什麼決定?」 mantissa 位數;多 1 bit 最大表示誤差減半
「accuracy 由什麼決定?」 執行的運算(rounding / 近似),與 mantissa 位數無關
「每個 exponent 區間有幾個代表數?」 2^N(N = mantissa 位數)
「為何 0 附近有空洞?如何解決?」 normalized 1.M 排除 0;用 denormalization(E=0 改 0.M)均勻填平
「denormalized 值怎麼算?」 (-1)^S × 0.M × 2^(2 - 2^(n-1));single → exp = −126
「abrupt underflow vs denormalization」 abrupt UF 把 E=0 全當 0 → 空洞更大;denorm 攤開最後區間 → 空洞消失
「E 全 1 / M=0」「E 全 1 / M≠0」 ±∞ / NaN
「什麼產生 NaN?」 0/00×∞∞/∞∞−∞、未初始化資料
「signaling vs quiet NaN」 signaling(MSB=0) 觸發 exception;quiet(MSB=1) 傳播不中斷;GPU 不支援 signaling
「正確捨入 add 的最大誤差?」 0.5 ULP
「round-to-zero (truncate) 的最大 ULP?」 1 ULP(截斷往 0,誤差可達整個最低位權)— 練習題 3
「除法 / sin 為何誤差 > 0.5 ULP?」 polynomial approximation 項數不足;補 0 的 mantissa bit
「early CUDA 為何不支援 denorm?」 硬體偵測+切換成本高(數千 cycle);cc 1.3+ 支援 double、cc 2.0+ 支援 single
「single → double 準確度差多少?」 mantissa 多 29 bit → 最大表示誤差降為 1/2^29