正規化の除算(非回復法)

[] [] []


正規化するときは商が±1以内に限られるので、通常より少ない引き算(小さい回路)で除算ができる。

1) 内積の正規化 -1ab / |a||b|=Σaj bj / sqrt(Σa2Σb2)+1

2) 正数の差と和の比 -1(a-b)/(a+b)+1

分子と分母が整数の場合の除算は正規化の除算(非回復法)に書いたとおりで、これを固定小数点に直したものは次のとおり。余りは切捨でなく丸め(四捨五入)とする。

a / d = q + e

分子a:符号つき、整数部ID+2ビット、小数部FDビット (-2*2IDa2*2ID-2-FD)

(-d≦a≦+dより広い範囲が必要だから整数部はID+2だけ必要、小数部はdと同じにしておく)

ただし、1+FQ<ID+FDのときは、-2*da(2-2-FQ-1)*dに限られる

(a/d(2*2ID-2-FD)/(2ID-2-FD )2-2-ID-FD、一方q+e2-2-FQ-1+2-FQ-1=2-2-FQ-1

 よって1+FQ<ID+FDのときはa/dの範囲よりq+eの範囲の方がせまくなる)

分母d:符号なし、整数部IDビット、小数部FDビット (2-FDd2ID-2-FD)

q: 符号つき、整数部2ビット、小数部FQビット (-2≦q≦2-2-FQ)

    (-1≦q≦1の範囲まで求めるためqの整数部は2ビット必要)

誤差e:丸めの場合 -2-FQ-1≦e2-FQ-1

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

#include <ap_int.h>

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

 * division: a/d = q + e

 *   param range: FQ<=ID+FD-1

 *   input range: -2<=a/d<2-2**(-FQ-1)

 *   error range: -2**(-FQ-1)<=e<2**(-FQ-1) (rounding)

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

template <int ID,int FD,int FQ>

ap_fixed<2+FQ,2> div_norm      // (o) quotient

  (ap_fixed<ID+FD+2,ID+2> a     // (i) dividend

  ,ap_ufixed<ID+FD,ID>    d     // (i) divisor

  ) {

  ap_int<ID+FD+2> r;

  ap_int<ID+FD+2> t;

  ap_uint<2+FQ> p;

  ap_fixed<2+FQ,2> q;

  for(int i=1+FQ; i>=0; i--) {

    if(i==1+FQ) {

      r = (ap_int<ID+FD+2>) a(ID+FD+1,0); // sign extention

    } else {

      r = (t(ID+FD,0),(bool)0);

    }

    p[i] = !r[ID+FD+1];

    t = (p[i])? (r - d(ID+FD-1,0)) : (r + d(ID+FD-1,0));

  }

  q(1+FQ,0) = ((ap_uint<2+FQ>)1<<(1+FQ))+ p + !t[ID+FD+1];

  return q;

}

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

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

  const int ID=2,FD=1;

#ifdef LIMITED_RANGE_OF_Q

  const int FQ=ID+FD-1-1;       // 1+FQ<ID+FD

#else

  const int FQ=ID+FD-1; // 1+FQ=ID+FD

#endif

  ap_fixed<ID+FD+2,ID+2> a;

  ap_ufixed<ID+FD,ID>    d;

  ap_fixed<2+FQ,2> q;

  double a_val,d_val,q_val,e_val;

  printf("-- ID=%-d FD=%-d FQ=%-d\n",ID,FD,FQ);

  /* a,dを全範囲振って商と誤差を表示する */

  for(int j=1; j<pow(2,ID+FD); j++) {

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

/* 入力値 */

      a = (double) i * pow(2,-FD);

      d = (double) j * pow(2,-FD);

      a_val = a.to_double();

      d_val = d.to_double();

      printf("-- a=%+f d=%+f",a_val,d_val);

      /* 1+FQ<ID+FDのとき大きいaはスキップする */

      if(a_val < d_val*(2-pow(2,-FQ-1))) {

  /* 計算実行 */

        q = div_norm<ID,FD,FQ>(a,d);

        /* 誤差 */

        q_val = q.to_double();

        e_val = a_val/d_val - q_val;

        printf(" q=%+f e=%+f",q_val,e_val);

        if(!(-pow(2,-FQ-1)<=e_val && e_val<pow(2,-FQ-1))) printf(" [FAIL]");

        }

      printf("\n");

    }

  }

  return 0;

}


[] [] []