53 / 63 · 概念
固定小数点・丸め・飽和
W,F で数値範囲を定め、guard・sticky による偶数丸めと丸め後の飽和を設計します。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
前提知識:符号付き加算・乗算のビット幅、算術右シフト、オーバーフローを理解していること。
ビット列の小数点位置を取り決める
固定小数点固定小数点 小数点位置をあらかじめ固定し、整数のビット列に一定の倍率を適用する数の表現です。幅・符号・小数部のビット数を合わせて定めます。は整数 X を と読む表現です。ハードウェアに小数点ビットを保存するわけではありません。ここでは signed 全幅 W に符号ビットを含め、F を小数ビット数とします。Q 表記は文献により整数部に符号を含むかが違うので、インターフェースには W,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 整数を掛け、小数ビット数を足します。
8 ビット F=4 同士の完全な積は signed 16 ビット F=8 です。8 ビット F=4 に戻すには、小数 4 ビットを減らしてから signed 8 ビット範囲を確認します。先に積を 8 ビットへ切ると、後段の飽和回路は元の大きさを判断できません。
切捨てと丸めの違い
d ビットを捨てる算術右シフトは で、負数も下へ丸めます。 なら正確な商は -1.5、q は -2 で、0 方向の切捨てとは違います。上位ビットを選ぶ方法が、負数でも正数と同じ誤差特性を持つと仮定してはいけません。
最も近い値へ丸め、ちょうど中間なら偶数を選ぶ round-to-nearest, ties-to-even を設計します。捨てる部分の最上位を guard g、残りの OR を sticky t、残す q の最下位を l とします。
g=0 なら半分未満なのでそのまま、g=1,t=1 なら半分超なので増加します。g=1,t=0 はちょうど半分で、q が奇数のときだけ 1 を足します。負数も同じ規則です。floor 商 q と剰余 は、常に を満たすためです。
例えば -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 に制限します。最後の格納まで広い幅を保つ必要があります。
- 入力 P=2040: 16 ビット · F=8
- スケール縮小: 2040 / 16 = 127.5
- 偶数丸め: 丸め後 raw = 128
- 飽和して格納: 整数表現 127 → 7.9375
丸めを終えてから飽和する
飽和は最大値超を最大値、最小値未満を最小値へ制限します。循環と違い、大きい正数が負数へ反転しません。ただし、既に幅を狭めて循環した値には正確な飽和を適用できません。
次は signed 16 ビット F=8 を signed 8 ビット F=4 にする完全な組合せモジュールです。d=4 固定なので guard=p[3]、sticky は p[2:0] の OR です。丸め増加と範囲比較を 17 ビット signed で行い、最後だけ 8 ビットを取ります。
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
endmoduleP=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 表現で完了させます。