sin,cos (CORDIC,回転モード)

[] [] []


CORDICによるsin,cosの計算はsin,cos (CORDIC,回転モード)に書いたとおりで、再掲しておく。

偏角a:符号つき、整数部3ビット、小数部FAビット

    -π a π

    FAは定数表atn[i]の精度に収める(FA≦30)

    単位をラジアンでなく12π1.0として2n等分にする場合は

    定数表atn[i]2πで割っておけばよい

余弦cと正弦s:符号つき、整数部2ビット、小数部Fビット

    -1≦ s,c ≦1

誤差:CORDICは収束計算なので、誤差は実際に計算して要確認。

   またsin,cosの変化率は大きいところでaと同じだから、

   同じくらいの精度(FAF)にする必要がある。

VitisHLSで合成可能なCコードは

#include <ap_int.h>

#include <cmath>  // atan()

/*************************************************************

 * c=cos(a) s=sin(a)

 *   param range: FA<=30. F should be comparable to FA.

 *   input range: -PI<=a<=PI

 ************************************************************/

template <int F,int FA>

void cordic_rot

    (ap_fixed<3+FA,3> a // (i) argument[radian]

    ,ap_fixed<2+F,2> *c // (o) cos

    ,ap_fixed<2+F,2> *s // (o) sin

    ) {

    /* atan table */

    ap_fixed<2+FA,2,AP_RND> atn[33];

    for(int i=-2; i<=30; i++) atn[i+2] = atan(pow(2.0,-i));

    /* initial value x0 */

    ap_fixed<1+F,1,AP_RND> x0 = 0.0658658286;

    /* cordic - rotation mode

     * x[0] = x0

     * y[0] = 0

     * t[0] = a

     * x[i+1] = x[i] - sign(t[i])*y[i]*2**(-i)

     * y[i+1] = y[i] + sign(t[i])*x[i]*2**(-i)

     * t[i+1] = t[i] - sign(t[i])*atan(2**(-i))

     * where sign(t) = (t>=0)? +1 : -1

     */

    ap_fixed<2+F,2>  x_tmp;

    ap_fixed<2+F,2>  y_tmp;

    ap_fixed<2+F,2>  x = x0;

    ap_fixed<2+F,2>  y = 0.0;

    ap_fixed<3+FA,3> t = a;

    for(int i=-2; i<=F; i++) {

        x_tmp = (x >> i);

        y_tmp = (y >> i);

        if(t[2+FA]) {

            x += y_tmp;

            y -= x_tmp;

            t += atn[i+2];

        } else {

            x -= y_tmp;

            y += x_tmp;

            t -= atn[i+2];

        }

    }

    *c = x;

    *s = y;

}

このコードは次のテストベンチで検証してある。

int main(int argc, char *argv[]) {

    const int F = 32;  // fractional part width of c,s

    const int FA = 30; // fractional part width of a (max 30)

    const double PI = 3.14159265358979;

    double a_val;

    double ec,ec_min=1.0,ec_max=-1.0,ec_min_a_val,ec_max_a_val;

    double es,es_min=1.0,es_max=-1.0,es_min_a_val,es_max_a_val;

    ap_fixed<3+FA,3> a; // (i) argument[radian]

    ap_fixed<2+F,2> c;  // (o) cos

    ap_fixed<2+F,2> s;  // (o) sin

    printf("-- F=%-d FA=%-d\n",F,FA);

    const int M = 12 * 1000;

    /* aPI/M刻みで振ってcos,sinの最大誤差を表示する */

    for(int i=-M; i<=M; i++) {

        a_val = PI * i / M;

        a = a_val;

        cordic_rot<F,FA>(a,&c,&s);

        ec = c.to_double()-cos(a_val);

        if(ec < ec_min) {ec_min = ec; ec_min_a_val = a_val;}

        if(ec > ec_max) {ec_max = ec; ec_max_a_val = a_val;}

        es = s.to_double()-sin(a_val);

        if(es < es_min) {es_min = es; es_min_a_val = a_val;}

        if(es > es_max) {es_max = es; es_max_a_val = a_val;}

    }

    printf("ec_min=%+3.1e at a=%6.3f\n",ec_min,ec_min_a_val/PI);

    printf("ec_max=%+3.1e at a=%6.3f\n",ec_max,ec_max_a_val/PI);

    printf("es_min=%+3.1e at a=%6.3f\n",es_min,es_min_a_val/PI);

    printf("es_max=%+3.1e at a=%6.3f\n",es_max,es_max_a_val/PI);

    return 0;

}


[] [] []