2

CORDICアルゴリズム〜三角関数等の数値計算法〜

525
0

CORDICアルゴリズム

これは、加減算,bitshiftとLUT(Look Up Table)(定数表)のみの計算で 三角関数(sin,cos),逆三角関数(arcsin,arccos),双曲線関数(sinh,cosh),逆双曲線関数(arcsinh,arcosh)などを求めることができる。
数3(複素数)を理解していれば、アイデアは割とすぐに理解できるだろう。

まず前提として、2進数において2k(k:)を掛ける操作は数字を小数点に対して右にk個移動することで実現できる。筆算を考えればわかるように、乗算や除算はシフト操作と加減算操作の組み合わせからなるが、加減算に比べて処理が大変である。テーブルの定数表を参照しながら、高速で三角関数の値を計算したいときに有効である。計算方法の導出について見ていくが、複素平面をイメージしながら読むと理解が進むと思う。 CORDIC(Qiita) 参照。
行列を用いた解説 があるが、複素数のほうが本質が理解しやすいように思うので、今回は複素数を用いた説明を試みる。

記号

絶対値r,偏角θの複素数をreiθ(=rcosθ+irsinθ)と表記する。

指数法則(三角関数の加法定理・複素数の乗除算)

r1eiθ1r2eiθ2=r1r2ei(θ1+θ2)
(reiθ)1=r1eiθ

この表記を用いて、求めるものを表してみる。
εk{1,1}, θ=k=0nεkθkのようにあるθkを用いてθが分解できたとする。このとき指数法則より(は総積記号)

eiθ=k=0neiεkθk=k=0n(cosθk+iεksinθk)=k=0ncosθk(1+iεktanθk)
ここでtanθk=2kとなるよう0<θk<π/2を選ぶ。そして
R0=k=0ncosθkとする。すると

計算手順の本質

eiθ=cosθ+isinθ=R0k=0n(1+iεk2k)

となる。この式がアルゴリズムの本質である。x,yを実数とすると、
(1+iεk2k)(x+iy)=(xεk2ky)+i(y+εk2kx)
となるが、よく見るとεk2ky,εk2kxはビットシフトと符号反転だけなので、1+iεk2kの乗算は加減算とビットシフトでできることがわかる。よって、

漸化式

Z0=R0+i0,  Zk+1=(1+iεk2k)Zk
cosθ=Re(Zn+1),  sinθ=Im(Zn+1)

とすると分解の結果から、cosθ=Re(Zn+1),  sinθ=Im(Zn+1)のようにして三角関数がn+1回の加減算とビットシフトで計算できる。分解については非回復型除算という手法で求める。
これは2進数の各桁の値が{1,1}のどちらかであるとして、除算を筆算で行うやり方とほぼ同じである。θk=arctan(2k)が大まかに指数関数的減少とみなせるので、θ1から順番に足すか引くかを部分剰余の符号から決定して次の部分剰余にフィードバックする再帰処理なので、詳細は省略する。誤差約2nで分解可能であることの正当性は この記事 で解説している。arctantanの逆関数である。

CORDICまとめ

 ①事前に計算して表に格納しておく:
k=0,...,n,  θk=arctan2k,  R0=k=0ncosθk
②入力θを非回復型除算によって次のように分解する:
θ=k=0nεkθk+ϵ  (εk{1,1},  ϵ,  |ϵ|<2n)
③加減算とビットシフトの繰り返しで複素数の乗算を行い、逐次Zkを計算する
Z0=R0+i0,  Zk+1=(1+iεk2k)Zk
cosθ=Re(Zn+1),  sinθ=Im(Zn+1)

③は特に、複素平面上では点0,Zk,Zk+1という鋭角θkの直角三角形が、εkの向きに斜辺と辺を共有する形で並んでいる姿を想像する。

10進数化

θkを分解基底と称する。分解θ=k=0nεkθk+ϵ   である範囲の任意のθを誤差ϵθn未満にできるとき、分解は完全であると称する。
分解の一般論と正当性は 「N進数の一般化について」 を参照してほしい。分解方法を工夫して10進CORDICについて考える。
さて、Minecraftで計算機を制作するにあたり、10進数計算のほうが信号強度の配線上のメリットや変換コスト、伝搬コストが少ないので、CORDICを10進数に改良する必要がある。
しかし安直に

k=0,,n,  θk=arctan10k,  R0=k=0ncosθk
θ=k=0nεkθk+ϵ  (εk{1,1},  ϵ)

としても、分解を行うことが不可能である(各桁に入る数字が2パターンしか無く、不足する)
今度は冗長10進数で次のように表そうとしてみる

k=0,,n,  θk=arctan10k,  R0=k=0ncosθk
θ=k=0nεkθk+ϵ  (εk{5,4,,4,5},  ϵ)

今度は誤差|ϵ|<10n程度で分解することができる。しかしsin(εkθk)=?εksinθkが成立しなくなるので、
eiθ=cosθ+isinθ=?R0k=0n(1+iεk10k)
が使えなくなる。|1+iεktanθk|εkに依らないためには、εk{1,1}にせざるを得ないのである。
そこで条件を少し緩める。ckを事前に決めた自然数として、θk=arctan(ck10k)とする。x,yを実数とすると、
(1+iεktanθk)(x+iy)=(xεk10kcky)+i(y+εk10kckx)
x,yのビットシフト及び、実部虚部ともにck+1回の加減算で実現できる。
次のようにcを構成すると完全に分解できる。

n=4m, k=4s+t, t{1,2,3,4}
(c1,c2,c3,c4)=(4,2,2,1)
θ0=arctan(1)
θk=arctan(ct10s+1)
正当性,誤差評価については 「N進数展開の一般化と分解について」 で既に説明している。

LUTに格納しておくのはθk  (t=1,2,4),  θ0,
R0=k=0ncosθkである。

実装の工夫

さらに線形近似を用いて必要なテーブル数を減らす。
θ=k=0nεkθk+ϵ=Θ+ϵ
としてΘ部分をCORDICにより計算し、ϵに対して線形近似を行う。
有効桁数をNとして|ϵ|<10N/2のとき次のように近似できる:
cosϵ=112ϵ2=1
sinϵ=ϵ.
これにより

線形近似

eiθ=(cosϵ+isinϵ)(cosΘ+isinΘ)=(cosΘϵsinΘ)+i(sinΘ+ϵcosΘ)

となる。ϵsinΘ,ϵcosΘは乗算が必要である。CORDICはレジスタのコピーや多数のビットシフトが必要なので、高速なバレルシフタが無いとシフトの律速になってしまう。乗算1回で済む場合はそちらを使うほうが有利である。

定数削減

10N/2<ct10s+1<10N/3のとき
θk=arctan(ct10s+1)=ct10s+113(ct10s+1)3=ct10s+1

となる。今回はラジアンではなくデグリーでの実装を試みたので、実際には
θkdeg=180ct10s+1πをsについてシフトさせて流用している。

双曲線CORDIC

実は複素数を用いると双曲線CORDICもセットで理解できる。
jを分解型複素数j2=1とする。分解型複素数の場合も同様の計算法則が成立する。
ejθ=coshθ+jsinhθ
θk=arctanh 2mk
θ=k=0nεkθk+ϵ  (εk{1,1},  ϵ)
eθ=coshθ+jsinhθ=R0k=0n(1+jεk2mk)
R0=k=0ncoshθk
Z0=R0+i0,  Zk+1=(1+jεk2mk)Zk
cosθ=Re(Zn+1),  sinθ=Im(Zn+1)
(1+jεktanhθk)(x+jy)=(x+εk2mkcky)+j(y+εk2mkckx)
ここで注意しなければならないのは、三角関数の場合と違い、
θk=arctanh 2k
を分解基底に取ると
k=2narctan(2k)>arctan(21)
k=2narctanh(2k)<arctanh(21)
なので分解が完全でない点である。 命題:基底の完全性 を参照。そこで、途中でkをダブらせて2回同じ分解を行うなどする必要がある。その意味でmkを用意した。
たとえば
(mk)=[1,2,3,4,4,5,6,7,8,9,10,11,12,13,13,14,15,16,17,18,19,20,21,22,23]
などとすれば完全である(greedyな算法)

投稿日:2024229
更新日:202431
OptHub AI Competition

この記事を高評価した人

高評価したユーザはいません

この記事に送られたバッジ

バッジはありません。
バッチを贈って投稿者を応援しよう

バッチを贈ると投稿者に現金やAmazonのギフトカードが還元されます。

投稿者

赤げふ
赤げふ
92
15635
東工大情報B4 数学,理論物理,Minecraft計算機/微分演算子の記事を書きます/主に表現論,量子群,物理の数理に興味があります

コメント

他の人のコメント

コメントはありません。
読み込み中...
読み込み中
  1. CORDICアルゴリズム
  2. 10進数化
  3. 実装の工夫
  4. 双曲線CORDIC