Altifigence Academy

53 / 63 · 概念

固定小数点・丸め・飽和

W,F で数値範囲を定め、guard・sticky による偶数丸めと丸め後の飽和を設計します。

前提知識:符号付き加算・乗算のビット幅、算術右シフト、オーバーフローを理解していること。

ビット列の小数点位置を取り決める

固定小数点固定小数点 小数点位置をあらかじめ固定し、整数のビット列に一定の倍率を適用する数の表現です。幅・符号・小数部のビット数を合わせて定めます。は整数 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 は整数 24 ではなく 1.5 です。同じ 0x18 でも F=3 なら 3 です。配線をつないでも数値の意味まで合うとは限らないため、各ポートのスケールを文書化します。

加算前に小数点位置をそろえます。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 に戻すには、小数 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 で、0 方向の切捨てとは違います。上位ビットを選ぶ方法が、負数でも正数と同じ誤差特性を持つと仮定してはいけません。

最も近い値へ丸め、ちょうど中間なら偶数を選ぶ 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 なら半分超なので増加します。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. 飽和して格納: 整数表現 127 → 7.9375

丸めを終えてから飽和する

飽和は最大値超を最大値、最小値未満を最小値へ制限します。循環と違い、大きい正数が負数へ反転しません。ただし、既に幅を狭めて循環した値には正確な飽和を適用できません。

次は signed 16 ビット F=8 を signed 8 ビット F=4 にする完全な組合せモジュールです。d=4 固定なので guard=p[3]、sticky は p[2:0] の OR です。丸め増加と範囲比較を 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 領域がないことを分けます。範囲外の部分選択を条件文に隠すだけでは、エラボレーションエラーを防げない場合があります。

丸めと飽和の方針はそれぞれ選びます。これらの処理には、加算器・比較器・MUXマルチプレクサ 選択信号に従い、複数の入力から一つを出力に接続する組合せ回路です。選択ビットと入力番号の対応を仕様で定めます。 詳しく見る のコストと遅延が伴います。AMD の固定小数点形式の文書にもこの区別があります。多項の累算では、中間幅を保持して最後に一度だけ変換するかを先に決めます。各段で飽和すると結合法則が崩れ、各段で丸めると小誤差が繰り返し蓄積する場合があります。

自分で考えてみましょう

signed 16 ビット F=8 の P を signed 8 ビット F=4 へ変換します。P=24,40,−24,−40,2040 の floor 商 q、guard/sticky、丸めた整数、最終 raw 出力と実数値を求めてください。2040 で先に飽和を検査すると何が問題ですか。

解説を見る

全入力の下位 4 ビットは 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 表現で完了させます。

選択はこのブラウザに適用されます。フッターからいつでも変更できます。