#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マイコン向けですので、あしからず。
最新コメント