Altifigence Academy

53 / 63 · 概念

定點數、捨入與飽和

以 W,F 格式定義數值範圍,設計 guard、sticky 的捨入至偶數,以及捨入後飽和。

先備知識:signed 加法與乘法的寬度、算術右移與 overflow。

以契約定義位元序列的小數點位置

定點數定點數預先固定小數點位置,對整數位元序列套用固定倍率來表示數值。必須一併指定位寬、是否帶符號與小數位元數。將整數 X 解讀為 x=X2−Fx=X2^{-F},硬體並沒有儲存小數點位元。本課定義 signed 總寬度 W 包含符號位,F 為小數位元數。不同文獻的 Q 記法對整數部分是否含符號位有差異,因此介面同時標 W,F 較清楚。

−2W−1−F≤x≤2W−1−F−2−F-2^{W-1-F}\le x\le 2^{W-1-F}-2^{-F}

W=8,F=4 時,間隔 1/16,範圍 −8~7.9375。此契約的 0x18 是 1.5,不是整數 24;同樣 0x18 若 F=3 就是 3。接好導線不代表數值意義也吻合,應記錄各連接埠的 scale。

加法前先對齊小數點。F 不同時,可把較小 F 的一方充分擴展再左移,或依指定捨入規則縮減較大 F。W、F 相同的兩數精確相加需要 W+1 位元,F 不變。乘法則相乘 raw 整數,小數位數相加。

P=XAXB,FP=FA+FBP=X_A X_B,\qquad F_P=F_A+F_B

兩個 8 位元 F=4 的完整乘積是 signed 16 位元 F=8。要輸出 8 位元 F=4,須先減少四個小數位,再檢查 signed 8 位元範圍。若先把乘積截成 8 位元,後續飽和電路便無法知道原始大小。

截除與捨入的差別

丟棄 d 位的算術右移為 q=⌊P/2d⌋q=\lfloor P/2^d\rfloor,負數也向下。例如 P=−24,d=4P=-24,d=4,精確商 −1.5,但 q=−2,與向零截斷不同。不能假設正數適用的「直接取高位」,對負數也有相同誤差性質。

設計 round-to-nearest, ties-to-even:取最近值,恰在中間時取偶數整數。被丟棄區的最高位為 guard g,其餘位元 OR 為 sticky t,保留 q 的最低位為 l。

inc=g∧(t∨l),qrnd=q+inc\mathrm{inc}=g\land(t\lor l),\qquad q_{\mathrm{rnd}}=q+\mathrm{inc}

g=0 表示小於一半,保持;g=1,t=1 表示大於一半,向上加 1;g=1,t=0 正好一半,只有 q 為奇數時加 1。負數也同規則,因採 floor 商 q 與餘數 r=P−q2dr=P-q2^d 時,永遠 0≤r<2d0\le r<2^d。

例如 −24/16=−1.5,q=−2 是偶數,結果 −2;−40/16=−2.5,q=−3 是奇數,結果仍 −2。「負數一律減 1」的額外規則反而錯誤。未飽和時,最終數值誤差不超過輸出 LSBLSB是位權最低的位元。須同時確認表示方向與索引規則。 詳細內容 的一半。Ties-to-even 不保證所有輸入分布的平均誤差必為 0,但可減少中點總向同側偏移的偏差。

將 16 位元 F=8 的 raw P=2040 轉成 8 位元 F=4:127.5 先捨入為偶數 128,再限為 signed 8 位元最大值 127。直到最後儲存前都要保留寬度。

先捨入,再飽和
  1. 輸入 P=2040: 16 位元 · F=8
  2. 縮小尺度: 2040 / 16 = 127.5
  3. 中間值取偶數: 捨入後 raw = 128
  4. 飽和後儲存: raw 127 → 7.9375

完成捨入後才做飽和

飽和將超過最大值者限為最大值,低於最小值者限為最小值。與 wrap 不同,大正數不會翻成負數。但已縮窄並回捲的值,無法再正確飽和。

以下完整組合模組將 signed 16 位元 F=8 轉成 signed 8 位元 F=4。d 固定為 4,guard=p[3]、sticky=p[2:0]。捨入遞增與範圍比較在 17 位元 signed 空間完成,最後才取 8 位元。

SystemVerilog
module requantize (
  input  logic signed [15:0] p,
  output logic signed [7:0] y,
  output logic saturated
);
  logic signed [16:0] wide, q, rounded;
  logic inc;
  always_comb begin
    wide = {p[15], p};
    q = wide >>> 4;
    inc = p[3] & ((|p[2:0]) | q[0]);
    rounded = q + (inc ? 17'sd1 : 17'sd0);
    saturated = 1'b0;
    if (rounded > 17'sd127) begin
      y = 8'sh7f;
      saturated = 1'b1;
    end else if (rounded < -17'sd128) begin
      y = 8'sh80;
      saturated = 1'b1;
    end else begin
      y = rounded[7:0];
    end
  end
endmodule

P=2040 時,P/16=127.5 先捨入到偶數 128,再飽和為 127,最終值為 7.9375。只檢查捨入前的 q=127,會漏掉這次超出範圍的情況。Saturated 表示數值超出範圍而被限制,不只是丟失低小數位。

泛化模組時,d=0 沒有待丟位元,d=1 沒有 sticky 區域,需分開處理。把越界 part-select 藏在條件式中,不一定能消除 elaboration 錯誤。

捨入與飽和增加加法器、比較器、MUX多工器是依選擇訊號,將多個輸入之一連至輸出的組合電路。必須明確定義選擇位元與輸入編號的對應關係。 詳細內容 的成本及延遲,需作為獨立政策選擇。AMD 定點數格式文件也有此區分。累加多項時,先決定是否保留中間寬度、最後只轉換一次。每級飽和可能破壞加法結合律,每級捨入則可能反覆累積小誤差。

自己試試看

將 signed 16 位元 F=8 的 P 轉成 signed 8 位元 F=4。P=24,40,−24,−40,2040 時,求 floor 商 q、guard/sticky、捨入整數、最終 raw 輸出與實際值。2040 若先做飽和檢查,有何問題?

查看解說

所有輸入低四位都是 1000,故 guard=1、sticky=0。P=24:q=1(奇數)→2,raw=2,實際值 0.125。P=40:q=2(偶數)→2,raw=2,0.125。P=−24:q=−2(偶數)→−2,raw=−2,−0.125。P=−40:q=−3(奇數)→−2,raw=−2,−0.125。P=2040:q=127(奇數)→128,超過 signed 8 位元最大值,故 raw=127、實際值 7.9375、saturated=1。若先只檢查 q=127,再在 8 位元加 1,可能回捲成 0x80 而輸出 −8;須在寬 signed 值完成遞增與比較。

此選擇適用於本瀏覽器,隨時可從頁尾變更。