#define TABLESIZE 128 //2の乗数であること
#define SUBBIT 8
#define SUBINDEX (1 << SUBBIT)
#define I_PI (TABLESIZE * SUBINDEX * 2)
#define I_HPI (TABLESIZE * SUBINDEX)
#define PI 3.1415926f
//int(sin(index / (pi / 2) * 128) * 65535 + 0.5)
const unsigned short sin_table[] = {
0, 804, 1608, 2412, 3216, 4019, 4821, 5623,
6424, 7223, 8022, 8820, 9616, 10411, 11204, 11996,
12785, 13573, 14359, 15142, 15924, 16703, 17479, 18253,
19024, 19792, 20557, 21319, 22078, 22834, 23586, 24334,
25079, 25820, 26557, 27291, 28020, 28745, 29465, 30181,
30893, 31600, 32302, 32999, 33692, 34379, 35061, 35738,
36409, 37075, 37736, 38390, 39039, 39682, 40319, 40950,
41575, 42194, 42806, 43411, 44011, 44603, 45189, 45768,
46340, 46905, 47464, 48014, 48558, 49095, 49624, 50145,
50659, 51166, 51664, 52155, 52638, 53113, 53580, 54039,
54490, 54933, 55367, 55794, 56211, 56620, 57021, 57413,
57797, 58171, 58537, 58895, 59243, 59582, 59913, 60234,
60546, 60850, 61144, 61429, 61704, 61970, 62227, 62475,
62713, 62942, 63161, 63371, 63571, 63762, 63943, 64114,
64276, 64428, 64570, 64703, 64826, 64939, 65042, 65136,
65219, 65293, 65357, 65412, 65456, 65491, 65515, 65530,
65535, 0
};
float mysin(float x){
long ix, subix, sign, tval;
ix = (int)(x * (I_PI / PI)); //単位変換
sign = ix & I_PI; //第3,第4象限である
ix &= (I_PI - 1); //第1,第2象限に限定
if(ix > I_HPI) ix = I_PI - ix; //第1象限に限定
subix = ix & (SUBINDEX - 1); //線形補完に用いるサブインデックス
ix >>= SUBBIT; //テーブル番号に変換
//線形補完
tval = ((long)sin_table[ix] * (SUBINDEX - subix)
+ (long)sin_table[ix+1] * subix);
return (sign ? -tval : tval) / (SUBINDEX * 65535.f);
}
マイコンでの動作に適した高速・コンパクトな三角関数です。
技術的な背景などは
こちらのページが参考になります。
ROM消費量をできるだけ減らし、高速かつ、そこそこの精度を目指して作ってみました。
128個のテーブル間を256分割して線形補完します。要は512角形で近似しています。
ROM節約と高速化のため、テーブルに格納する値を16bit整数としています。
浮動小数点計算は2回、条件分岐も可能な限り減らしました。
コンパイラによる最適化を前提として、定数同士の演算はそのまま残してあります。
STM32で64MHz、GCCで-Osで最適化した時の処理時間は約7us、ROM消費量は約350Byteとなりました。
最大誤差は0.00005ぐらいでした。
テーブルのビット数を上げるか、配列数を増やせばもう少し精度は向上するでしょう。
ROM消費量は増えますが・・・
あと、最初の式の"PI"を"180.f"に置き換えると度数法にできます。
あ、ちなみに整数乗算器を持った32bitマイコン向けですので、あしからず。