FFTを固定小数点化する(4)
前回の話を元に、大浦さんのFFTを書き換えてみましょう。下のコードは前半をとりだしたものです。
/* based on http://momonga.t.u-tokyo.ac.jp/~ooura/fftman/ftmn1_2.html#sec1_2_1 */ void fft(int n, double theta, double ar[], double ai[]) { int m, mh, i, j, k; double wr, wi, xr, xi; for (m = n; m > 1; m = m/2 ) { /* n, n/2, n/4, ... , 2 */ mh = m/2; /* n/2, n/4, n/8, ..., 1 */ for (i = 0; i < mh; i++) { wr = cos(theta * i); wi = sin(theta * i); for (j = i; j < n; j += m) { k = j + mh; xr = ar[j] - ar[k]; xi = ai[j] - ai[k]; ar[j] += ar[k]; ai[j] += ai[k]; ar[k] = wr * xr - wi * xi; ai[k] = wr * xi + wi * xr; } } theta *= 2; }
このプログラムを前回の話を元に書き換えます。ポイントは
- doubleをshortに置き換える
- 乗算後15bit右シフトする
- 絶対値1未満の値は32768倍して格納する
の、3点です。結果は以下の通り。
/* based on http://momonga.t.u-tokyo.ac.jp/~ooura/fftman/ftmn1_2.html#sec1_2_1 */ void fft(int n, double theta, short ar[], short ai[]) { int m, mh, i, j, k; short wr, wi, xr, xi; for (m = n; m > 1; m = m/2 ) { /* n, n/2, n/4, ... , 2 */ mh = m/2; /* n/2, n/4, n/8, ..., 1 */ for (i = 0; i < mh; i++) { wr = cos(theta * i) * 32768; // ここ wi = sin(theta * i) * 32768; // ここ for (j = i; j < n; j += m) { k = j + mh; xr = ar[j] - ar[k]; xi = ai[j] - ai[k]; ar[j] += ar[k]; ai[j] += ai[k]; ar[k] = ( wr * xr - wi * xi ) >> 15; // ここ ai[k] = ( wr * xi + wi * xr ) >> 15; // ここ } } theta *= 2; }
一番内側のループはバタフライ演算を実行する部分です。この部分には積が2行でつかわれていますので、その両者で15bitのシフトをおこなっています。
複素係数であるwr/wiはshortに変更していますが、この変数への係数生成はcos/sin関数を残しています。次回はこれをテーブル参照に変更しましょう。