ショボい計算環境でソレナリの計算結果を算出する方法

デカいパソコンで計算するときなら何にも考えなくてもヨイのだけど、小さなマイコンで計算する時は計算能力がかなり限られているので、それに見合った手法を取らないと無駄に時間を使ってしまい、間に合わなくなるコトが多い。そんな時に使ういくつかのコツを並べておこう。

●精度問題

通常、マイコンの計算は整数で行われている。でも、世の中の事は実数が必要な場合が多い。つまり、小数点以下ナンボってお話ね。で、最近のC言語なんかの開発環境だと実数にも対応してて、例えば1.2なんて数値も扱えたりする。しかし、多くの場合でソレをやるとメモリをバカ喰いしたり、パフォーマンスが急に低下したりする。

なぜかというと、マイコンの側がそういうモンに対応しているとは限らず、対応していない場合は計算用のライブラリがリンクされ、そこでかなりの時間を消費してしまうからだ。また、それらが動作するのに必要なメモリも無駄に消費してしまうコトになる。

で、実数にも対応しているマイコンに交換できるなら無論それが一番ではあるのだけど、コスト問題やサイズ問題(概してそういうのはパッケージがデカい)なんかで使えない場合にどうするか…である。

一番簡単な方法は、必要な精度を考えその精度で計算するのに必要なレベルまで一旦乗算し、あとで除算して辻褄をあわせるというもの。例えば1.2は256倍すれば307.2。これを307として計算を進め、あとで1/256すれば良い。256倍するには左シフト8ビット、逆は右シフト8ビットすれば済むから、計算は一瞬ですね。当然、加減算する値も全部シフトしてから計算しないとダメでっせ(笑)。乗除算は状況に応じてシフトするかしないか決めないといけないけど…理由はわかりますよね(^_^;)?

これは、有効桁数的に1.2が2桁もあれば済むという点を認識した上で、307.2を307に丸めても実質的に大きな問題にはならないであろうと考えての実装なのね。当然、計算結果の有効数字も2桁しかないワケだけど。

●割り算問題

先の項でも出てたけど、マイコンで計算させる時に最も問題になるのは割り算かなと思う。2のベキ乗の数値で割るだけならシフトすれば済むのだけど、では5で割るにはどうするか…となった時に困る。なぜ困るのかというと、マイコンには機械語レベルで割り算を実装していないものがあるから。こういうマイコンでC言語とかで実装すると、迂闊に割り算を書いたとたんに割り算のライブラリがリンクされ、思いっきり時間が掛かるコトが多い。

最初に考えるベキは、できるだけ割り算を使わないで済むアルゴリズムを考えるコト。場合によっては、ちょっとした工夫で割り算が不要になるコトがある。そうなればシメたものだ。

次に考えるベキは、掛け算に置き換えられないか…かな。大昔ならともかく、今は掛け算ぐらいまでは実装されてるコトが多い。また、シフトは先にも使った通り昔から普通に実装されている機能になる。であれば、割り算を「掛け算とシフトの組み合わせ」でなんとかできないかと考える。

例えば5で割りたい場合、1/5に近似できるn/2^mを考える。一例を挙げるなら、3/16だろうか。3/16は0.1875なので、「3倍して右シフト4ビット(=/16)」すれば、実質的に0.1875を乗算したのと同じコトになる。5で割るのは0.2を乗算するコトなので、有効桁数1桁のレベルであれば、これでも概ね対応できる。

あとは必要な精度が出るように、シフトする大きさをそれなり増やしていけば、それなりに計算結果が出てくると思う。51/256なら0.1992…なので、51倍して右8シフトであれば結構精度高くなるよね。

●三角関数

これも結構面倒なお話なんだが、実はちょっとした工夫で割りとどうにでもなる。

まず、角度的な精度がどれだけ必要なのかを考えよう。仮にそれが1度単位で良いのであれば、360度分の計算結果を先に求め、固定的な配列で宣言し、その初期値としてソースコード上に全ての答えを乗せたものを作成すれば、添え字に角度を入れるだけで答えが出るよね。無論、ここも実数が嫌なら先の手法で何倍かした数値が出るようにすればよい。

先にこういうの計算させるには、表計算ソフトとか使ってザーっと計算させ、その結果をcvsファイルか何かで取り出し、加工してソースにコピペするのが一番楽ぢゃないかな。

ちなみに、メモリが少ない場合、実は90度分のデータがあれば良いコトに気付くだろうか。また、sinのデータがあればcosも生成できるよね。

あと、これはこのお話とは少し別件なんだが、こういった大きなサイズの配列を宣言する場合、普通に宣言して初期化するコードを書くと全てRAM上に展開されるコトが多く、これはRAMのメモリ不足を招きやすい。

一般に最近のマイコンはROMはアホほど積んであってもRAMは少ないので、できるだけRAMの消費を減らしたい。こう言う場合、C言語ならconstで宣言すればROM上に展開されるコトが多いのも、覚えておいた方がイイかな。配列へのポインタが直接ROM上のデータを指すので、RAMは消費されないコトになる。この手の書き換え可能性のないテーブルは全部そうやっておくのがオススメ。もしかすると、static constにしないとダメな環境があるかもしれないが…。

とりあえず、思いつくままに並べてみた。また何か思い出したら書き足すコトにする。

(未完)

Zak について

基本的にヲタクです。いや、別に萌えとかいうのではなく、ハマるとトコトン進めようとする癖があるので、自制が必要だという…。
カテゴリー: RCJ OYAJI, ソフトウエア, なんか作る, マイコン パーマリンク