前回, 整数型しか扱えないマイコンで平方根を求める方法を述べましたが, 別の時, 自然対数を求めたいことがありました. その時得た Tips です.
幸い精度はあまり必要なかったので, テーブル参照を使ったのですが得たい範囲を均等に分割した単純なテーブルだと 使い物になりません. そこで若干工夫することにしました (実際工夫してあるアルゴリズムをネットで 見つけ再実装しただけですが…).
このアルゴリズムは, 整数型 (2 進数) の nbit 目は 2^n を表していることを 利用してます. まず 0 でなく 1 となっている一番大きい桁を探します.
といった感じで組んだコードが以下の通りです. 整数型で返すため 2048 倍された値を返すようにしています.
int Log(int a){ int Tbl[32] = { // 8192 log (1+N/32) のテーブル 0, 252, 497, 734, 965, 1189, 1408, 1621, 1828, 2030, 2227, 2420, 2609, 2793, 2973, 3149, 3322, 3490, 3656, 3818, 3977, 4133, 4286, 4437, 4584, 4729, 4872, 5012, 5150, 5285, 5418, 5549 }; if (a<=1) return 0; int m=0; int k=1; int x=a; int y; while (x>=k){ // 最上位 bit を求める m++; k<<=1; } m--; k>>=1; x ^= k; if (m>5){ x >>= m-5; y = (x + 0x20) << (m-5); } else if (m<5){ x <<= 5-m; y = (x+0x20) >> (5-m); } else { y = x + 0x20; } // これで a を (1 + x/32) 2^m で表現したときの x と m と // y = (1 + x/32) 2^m が求まる m *= 5679;// 8192 log(2) m += Tbl[x]; // + 8192 log(1+x/32) m <<=2; // 8192 →2048 // 補間処理 y = a-y; if (y) m += (2048*y)/a; return m; } |