FFTを固定小数点化する(3)

前回、16bit固定小数点数と整数のフォーマットの違いを見ました。これで両者の差がわかったわけですが、C/C++言語で固定小数点数を使えないことに変わりはありません*1
そこで、代表的な操作について一つ一つどうすれば良いのか見て行きましょう。

入れ物

16bit固定小数点数を格納すべき変数の型は何でしょうか。結論から言えばshort型です。
short型は整数として取り扱われ、多くのコンパイラで16bitとして扱われます。実際、VisualDSP++ for Blackfin、bfin-elf-gccのいずれもがshortを16bit整数型として扱います。移植性の面からshortと言い切ってしまうのが嫌な方はtypedefでもマクロでも使うとよいでしょう。ここではshortを使います。

加減算

short型に16bit固定小数点数が格納されているとします。加算はどうすれば良いでしょう。答えは簡単。何も考えずに加算を実行します。

short a, b, c; // 16bit固定小数点数が格納される

c = a + b; // 16bit固定小数点加算

加算は小数点の位置を動かさないため、これでかまいません。きちんと正しい和をえられます。
減算の場合も同じです。

乗算

乗算の場合、小数点の位置が問題になります。C言語では整数しか取り扱えず、その場合小数点の位置はLSBの右です。一方固定小数点数では小数点はMSB*2の右にあります。この状態で整数乗算を行うと、仮想的な固定小数点がbit30の右まで動いてしまいます。わかりにくい方は筆算で計算して見るといいでしょう。
そこでこの固定小数点の移動を補正するため、C言語で16bit固定小数点数を乗算する場合には必ず積を15bit右に動かします。

short a, b, c; // 16bit固定小数点数が格納される

c = a * b >> 15; // 16bit固定小数点乗算

C言語ではshort型の演算はintに拡張されて計算されますので、一般にはオーバーフローの心配はありません。ただし、小型の組み込みコアの場合、C言語が16*16=>32bitの積に対応していない可能性もありますので、必ず調べておいてください。

除算

除算の場合は乗算の逆です。あらかじめ固定小数点の位置を整数除算にあうよう15bit左に動かして補正しておきます。

short a, b, c; // 16bit固定小数点数が格納される

c = ( a << 15 ) / b ; // 16bit固定小数点乗算

除算の場合、必ず被除数が除数より大きな絶対値を持たなければなりません。そうでない場合には結果の範囲が±1の範囲を逸脱し、固定小数点数で表現できなくなります。

リテラル

以上のように演算については工夫によってC言語で対応できることがわかりました。後はリテラルのことだけ考えればOKです。
リテラルは少々頭の痛い問題です。コンパイラが固定小数点型に対応していませんから、固定小数点リテラルを以下の様に使うことはできません。

short a = 0.5;

そこで、何らかの工夫が必要になります。結論から言えば、以下の様に工夫することでこの問題も解決できます。

short a = 0.5*32768;

32768は15bitシフトに相当する係数です。0.5は浮動小数点数なので、シフト演算を行うことはできません。しかし、ここはコンパイラ時に最適化されますので、まっとうなコンパイラならば実行時には浮動小数点演算を行いません。単に0x2000がロードされるだけです。
この表記は可読性が悪いので適当なマクロでくるんでやるほうが良いでしょう。たとえば、

#define _f(x) (x*32768)

short a = _f(0.5);

とするだけで、随分読みやすくなります。

*1:最新のC言語標準では使えるが、C++では使えないし実装しているコンパイラも多くないのでここでは無視する

*2:bit15