Fixed-Point Designer

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

固定小数点演算の実行

この例では、基本的な固定小数点演算の実行方法を説明します。

加算と減算

2 つの符号なし固定小数点数を加算するときは、結果を正確に表すためにキャリー ビットが必要となる場合があります。このため、2 つの B ビット数 (同じスケーリングをもつ) を加算すると、結果の値は、使用される 2 つのオペランドと比べてビットが多くなります。

a = ufi(0.234375,4,6);
c = a + a
c = 

    0.4688

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Unsigned
            WordLength: 5
        FractionLength: 6
a.bin
ans =

1111

c.bin
ans =

11110

符号付きの 2 の補数を使用すると、結果を正確に表すために必要な符号拡張のため、同じような状況が発生します。

a = sfi(0.078125,4,6);
b = sfi(-0.125,4,6);
c = a + b
c = 

   -0.0469

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 5
        FractionLength: 6
a.bin
ans =

0101

b.bin
ans =

1000

c.bin
ans =

11101

精度が異なる 2 つの数値を加算または減算する場合は、最初に基数点を揃えてから演算を行う必要があります。その結果、演算の結果とオペランドには複数のビットの違いがあります (基数点がどれだけ遠いかによって異なります)。

a = sfi(pi,16,13);
b = sfi(0.1,12,14);
c = a + b
c = 

    3.2416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 18
        FractionLength: 14

加算と減算に関するその他の考慮事項

以下のパターンは "推奨しない" ことに注意してください。スカラー加算は for ループ内の各反復で実行されるので、各反復でビットが temp に追加されます。結果として、ceil(log2(Nadds)) のビット成長率ではなく、ビット成長率は Nadds に等しくなります。

s = rng; rng('default');
b = sfi(4*rand(16,1)-2,32,30);
rng(s); % restore RNG state
Nadds = length(b) - 1;
temp  = b(1);
for n = 1:Nadds
    temp = temp + b(n+1); % temp has 15 more bits than b
end

sum コマンドを代わりに使用すると、ビット成長率は予想どおりに制限されます。

c = sum(b) % c has 4 more bits than b
c = 

    7.0059

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 36
        FractionLength: 30

乗算

一般に、完全精度の積には、オペランドの語長の合計と同じ語長が必要です。以下の例では、積 c の語長は、a の語長に b の語長を加算した長さと等しいことに注意してください。c の小数部の長さも、a の小数部の長さに b の小数部の長さを加算した長さと等しくなります。

a = sfi(pi,20);
b = sfi(exp(1),16);
c = a * b
c = 

    8.5397

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 36
        FractionLength: 30

代入

事前定義された変数に固定小数点値を代入するときは、量子化が行われる場合があります。このような場合は、必要に応じて左辺へ代入される前に、最も近い整数に丸められて飽和することで、式の右辺が量子化されます。

N = 10;
a = sfi(2*rand(N,1)-1,16,15);
b = sfi(2*rand(N,1)-1,16,15);
c = sfi(zeros(N,1),16,14);
for n = 1:N
    c(n) = a(n).*b(n);
end

a(n).*b(n) を完全精度で計算すると、32 の語長と 30 の小数部の長さをもつ中間結果が生成されることに注意してください。前に説明したように、その結果は、16 の語長と 14 の小数部の長さに量子化されます。量子化された値は、要素 c(n) に代入されます。

結果の明確な量子化

追加のロジック/計算が必要となる場合、結果を量子化するときに最も近い整数への丸めまたは飽和が、不適切である可能性もあります。また、左辺値に割り当てて量子化を実行することが不適切な場合もあります。このような場合は、QUANTIZE を使用できます。一般的な例はフィードバック ループです。量子化を行わない場合は、指定される入力データが増えると、ビット成長率に制限がなくなります。

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
for n = 1:length(x)
    z    = y(n);
    y(n) = x(n) - quantize(a.*z, true, 16, 14, 'Floor', 'Wrap');
end

この例では、積 a.*z を完全精度で計算し、次に 16 ビットの語長と 14 の小数部の長さに量子化します。負の無限大への丸め (切り捨て) とオーバーフローが発生した場合のラッピングによって量子化が行われます。量子化は割り当て時にも発生します。これは、式 x(n) - quantize(a.*z, ...) が 18 ビットの中間結果を生成し、y が 16 ビットとなるように定義されているためです。割り当て時の量子化を回避するために、以下に示すように明確な追加量子化を取り入れることができます。これを行う利点は、最も近い整数への丸めまたは飽和ロジックが使用されないことです。左辺の結果は y(n) と同じ 16 ビットの語長と 14 の小数部の長さをもつので、量子化が不要です。

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
T = numerictype(true, 16, 14);
for n = 1:length(x)
    z    = y(n);
    y(n) = quantize(x(n), T, 'Floor', 'Wrap') - ...
           quantize(a.*z, T, 'Floor', 'Wrap');
end

完全精度ではない和

完全精度の和は適切であるとは限りません。たとえば、C コードが生成される場合、上記の x(n) - quantize(...) の中間結果に対応する 18 ビットの語長は、複雑で効率の悪いコードとなる可能性があります。ただし、16 ビットに対する加算/減算の結果をすべて保持するためには適切である場合があります。この目的には、関数 accumpos および accumneg を使用できます。

a = sfi(0.1,16,18);
x = sfi(2*rand(128,1)-1,16,15);
y = sfi(zeros(size(x)),16,14);
T = numerictype(true, 16, 14);
for n = 1:length(x)
    z    = y(n);
    y(n) = quantize(x(n), T);                 % defaults: 'Floor','Wrap'
    y(n) = accumneg(y(n), quantize(a.*z, T)); % defaults: 'Floor','Wrap'
end

アキュムレータのモデル化

関数 accumpos および accumneg は、アキュムレータのモデル化に適しています。動作は C の演算子 += および -= に対応しています。一般的な例は、係数および入力データを 16 ビットで表現する FIR フィルターです。乗算は完全精度で実行され、32 ビットと 8 ガード ビットのアキュムレータが生成されます。つまり、合計 40 ビットが使用されるので、オーバーフローを発生させずに最大 256 の累積が可能になります。

b = sfi(1/256*[1:128,128:-1:1],16); % Filter coefficients
x = sfi(2*rand(300,1)-1,16,15);     % Input data
z = sfi(zeros(256,1),16,15);        % Used to store the states
y = sfi(zeros(size(x)),40,31);      % Initialize Output data
for n = 1:length(x)
    acc = sfi(0,40,31); % Reset accumulator
    z(1) = x(n);        % Load input sample
    for k = 1:length(b)
        acc = accumpos(acc,b(k).*z(k)); % Multiply and accumulate
    end
    z(2:end) = z(1:end-1); % Update states
    y(n) = acc;            % Assign output
end

行列の算術演算

構文を簡略化してシミュレーション時間を短縮するために、行列の算術演算を使用できます。FIR フィルターの例では、内側のループを内積に置き換えることができます。

z = sfi(zeros(256,1),16,15); % Used to store the states
y = sfi(zeros(size(x)),40,31);
for n = 1:length(x)
    z(1) = x(n);
    y(n) = b*z;
    z(2:end) = z(1:end-1);
end

内積 b*z は完全精度で実行されます。これは行列演算なので、ビット成長率は含まれる乗算と乗算結果の加算の両方に起因します。したがって、ビット成長率はオペランドの長さによって異なります。b および z の長さは 256 なので、加算により 8 ビット成長率となります。これが、内積が 32 + 8 = 40 ビット (小数部の長さは 31) となる理由です。これは y が初期化される形式なので、代入 y(n) = b*z では量子化が起こりません。

256 より多い係数に対して内積を行わなければならなかった場合、ビット成長率は 8 ビットを上回り、乗算に必要な 32 ビットを超えます。40 ビットのアキュムレータのみをもっていた場合は、y(n) = quantize(Q,b*z) のような量子化器を取り入れるか、または前述の関数 accumpos を使用することで、動作をモデル化できます。

カウンターのモデル化

関数 accumpos を使用して、最大値に達すると必然的にラッピングする簡単なカウンターをモデル化できます。たとえば、以下のように 3 ビットのカウンターをモデル化できます。

c = ufi(0,3,0);
Ncounts = 20; % Number of times to count
for n = 1:Ncounts
    c = accumpos(c,1);
end

3 ビットのカウンターは 7 に達すると必然的にゼロに戻るので、カウンターの最終値は mod(20,8) = 4 です。

他の組み込みデータ型を使用した計算

fi * double

fidouble の間で演算を行うと、double は、fi と同じ語長と符号、そして最高精度の小数部の長さをもつ fi にキャストされます。演算の結果は fi になります。

a = fi(pi);
b = 0.5 * a
b = 

    1.5708

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 32
        FractionLength: 28

MATLAB® と C の相違点

C では、整数データ型と double データ型の間での演算の結果は double に変換されることに注意してください。

ただし、MATLAB では、組み込み整数データ型と double データ型の間の演算の結果は、整数になります。この点で、fi オブジェクトは MATLAB の組み込み整数データ型のように動作します。fi と double の間の演算の結果は fi になります。

fi * int8

fi と組み込み整数データ型 [u]int[8,16,32] のうちの 1 つとの間で演算を行うと、整数の語長と符号付き/なしが保持されます。演算の結果は fi になります。

a = fi(pi);
b = int8(2) * a
b = 

    6.2832

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 24
        FractionLength: 13