符号つき除算(非回復法)

[] [] []


前項の回復法では引きすぎたら引く前の値に戻したが、戻さずに次の桁で足しても同じ結果になる(例えば-1/8=-1/2+1/4+1/8)。この方法は引き放し法非回復法Non-Restoringとよばれる。

非回復法では、商はビットごとに0,1でなく+1,-1で得られるので、次の手順で0,1に直せばよい

+1のビットをp[N-1:0]とおくと、-1のビットは~p[N-1:0]だから

q[N:0] = p[N-1:0]-(~p[N-1:0])

    = p[N-1:0]+{1,p[N-1:0]}+1

    = (1<<N)+{p[N-1:0],1}

) q=(+1)*22+(-1)*21+(-1)*20 =1のときp[2:0]=100

  ∴q[3:0]=1000+{100,1}=0001

前項の回復法の例を非回復法で手計算すると次のようになる。

【非回復法】 符号なし/符号なし

説明

例)a[2:0]=101=(5)  d[1:0]=11=(3)

          _______ 

       11)00101.0  r={00,a[2]}

q[2]=+1 -) 11      d

          ---

          110      t=r-d <0

           100     r={r[1:0],a[1]}

q[1]=-1  +) 11     d

           ---

           111     t=r+d <0

            111    r={r[1:0],a[0]}

q[0]=-1   +) 11    d

            ---

            010    t=r+d >=0

             10.0  r={t[1:0],0}

q[-1]=+1   -) 1.1  d

            -----

             00.1  t=r-d >=0

 

切捨の場合q[2:0]=+4-2-1=1

丸めの場合q[2:-1]=+4-2-1+0.5=1.5→2

 

 

引き算はq[IA-1](aMSB)から始める

負だったらq[i]=-1として分母を足す

 非負だったらq[i]=+1として分母を引く

 if(r<0) q[i]=-1 t=r+d

 else    q[i]=+1 t=r-d

 符号を判定するためt,rのビット幅は[ID:0]だけ必要

 

 

切捨(0<=e<1)の場合はq[0]まで求めて終わり

 +1のビットをp[i]とおくとq[3:0]=1000+{p[2:0],1}

 t<0(引きすぎ)のときはq=q-1にすればよいので

 if(t<0) q[3:0]=1000+{p[2:0],0}

 else    q[3:0]=1000+{p[2:0],1}

←-丸め(-0.5<=e<0.5)の場合はq[-1]まで求めて

 if(t<0) q[3:-1]=10000+{p[2:-1],0}

 else    q[3:-1]=10000+{p[2:-1],1}

 さらにq[3:0]=q[3:0]+q[-1]とするので

 if(t<0) q[3:0]=1000+p[2:-1]

 else    q[3:0]=1000+p[2:-1]+1

回復法は減算だけ使うのに対して、非回復法は加減算両用の組合せ回路を使うので動作速度が遅くなりそうだが、正÷正だけでなく正÷負なども同じ回路で扱える利点がある。そこで、符号つき整数の除算を考える

a / d = q + e (d>0)  a / d = q - e (d<0)

分子a:符号つき、整数部IAビット

分母d:符号つき、整数部IDビット

q: 符号つき、整数部IQビット、小数部FQビット

     (1) a,dが任意の値をとりうる場合は-2IA-1/1a/d(-2IA-1)/(-1)だからIQ=IA+1だけ必要

     (2) -2IQ-1a/d2IQ-1-2-FQとわかっている場合はビット幅IQ<IA+1で十分

     例) N個の値xi の平均値はxiの値の範囲内に必ず収まる

誤差e(1) 切捨の場合 0 ≦e2-FQ ただしIQ<IA+1のときa=2IQ-1,d=-1だけe=2-FQになる

     (2) 丸めの場合 -2-FQ-1≦e2-FQ-1

手順は、分子と分母が同符号ならば引いて異符号なら足していけばよい

簡単な例としてIA=3,ID=2,IQ=IA+1=4,FQ=0の場合で手計算すると次のようになる。(/負や余り0の例は→補足)

【非回復法】 正/

説明

例)a[2:0]=010=( 2) d[1:0]=11=(-1)

          _______ 

       11)00010.0  r=符号拡張a[2]>=0

q[2]=-1 +)111      符号拡張d

          ---

          111      t=r+d <0

           111     r={t[1:0],a[1]}

q[1]=+1  -)111     符号拡張d

           ---

           000     t=r-d >=0

            000    r={t[1:0],a[0]}

q[0]=-1   +)111    符号拡張d

            ---

            111    t=r+d <0

             11.0  r={t[1:0],0}

q[-1]=+1   -)11.1  符号拡張d

             ----

             11.1  t=r-d

 

切捨の場合q=-4+2-1=-3(余り-1)→-2

丸めの場合q=-4+2-1+0.5=-2.5(余り-0.5)→-2

 

 

引き算はq[IA-1](aMSB)から始める

異符号だったらq[i]=-1にして分母を足す

 同符号だったらq[i]=+1にして分母を引く

 if(rdが異符号) q[i]=-1 t=r+d

 else       q[i]=+1 t=r-d

 符号を判定するためt,rのビット幅は[ID:0]だけ必要

 

 

 

切捨(0<=e<1)の場合はq[0]まで求めて終わり

 +1のビットをp[i]とおくとq[3:0]=1000+{p[2:0],1}

 t<0(引きすぎ)のときは、d>0ならばq=q-1,

 d<0ならばq=q+1とすればよいので

 if(t<0) q[3:0]=1000+{p[2:0],0}+(d<0)?10:0

 else    q[3:0]=1000+{p[2:0],1}

丸め(-0.5<=e<0.5)の場合はq[-1]まで求めて

 if(t<0) q[3:-1]=10000+{p[2:-1],0}+(d<0)?10:0

 else    q[3:-1]=10000+{p[2:-1],1}

 d>=0ならばq[3:0]=q[3:0]+q[-1](切上げ),

 d<0ならばq[3:0]=q[3:0](切捨て)とすればよいので

 if(t<0) q[3:0]=1000+p[2:-1]+(d<0)? 1:0

 else    q[3:0]=1000+p[2:-1]+(d<0)? 0:1

ここで引き算の始まりをqMSB q[IQ-1]=q[IA]ではなくq[IQ-2]=q[IA-1]にしているのは、非回復法ではp[IQ-2:0]からq[IQ-1:0]が求まるからである。

◇◇

このアルゴリズムをVitisHLSで合成可能なCコードで書くと次のようになる。ここで商qを小数点以下まで求めるためにa*2FQ/d=qaを左シフトしている。

#include <ap_int.h>

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

 * division: a/d = q + e (d>0) , a/d = q - e (d<0)

 *   param range: IQ<=IA+1

 *   input range: d!=0,-2**(IQ-1)<=a/d<=2**(IQ-1)-2**(-FQ)

 *   error range: 0<=e<2**(-FQ) (truncation)

 *   ただしIQ<IA+1のときa=2**(IQ-1),d=-1だけe=2**(-FQ)になる

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

template <int IA,int ID,int IQ,int FQ>

ap_fixed<IQ+FQ,IQ> div_int_trn // (o) quotient

  (ap_int<IA> a // (i) dividend

  ,ap_int<ID> d // (i) divisor

  ) {

  ap_int<IA+FQ> a_tmp = (ap_int<IA+FQ>) a(IA-1,0)<<FQ;

  ap_int<ID+1> d_tmp = (ap_int<ID>) d(ID-1,0);

  ap_int<ID+1> r;

  ap_int<ID+1> t;

  ap_uint<IQ+FQ> p;

  ap_fixed<IQ+FQ,IQ> q;

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

    if(i==IQ+FQ-2) {

      r =(ap_int<IA-IQ+2>) a_tmp(IA+FQ-1,i); //sign-extension

    } else {

      r = (t(ID-1,0),a_tmp[i]);

    }

    p[i] = (r[ID]==d_tmp[ID]);

    t = (p[i])? (r - d_tmp):(r + d_tmp);

  }

  q(IQ+FQ-1,0) = ((ap_uint<IQ+FQ>) 1<<(IQ+FQ-1))

                +(p,!t[ID])+((t[ID] & d_tmp[ID])?2:0);

  return q;

}

#include <ap_int.h>

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

 * division: a/d = q + e (d>0) , a/d = q - e (d<0)

 *   param range: IQ<=IA+1

 *   input range: d!=0, -2**(IQ-2)<=a/d<=2**(IQ-2)

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

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

template <int IA,int ID,int IQ,int FQ>

ap_fixed<IQ+FQ,IQ> div_int_rnd // (o) quotient

  (ap_int<IA> a // (i) dividend

  ,ap_int<ID> d // (i) divisor

  ) {

  ap_int<IA+FQ+1> a_tmp=(ap_int<IA+FQ+1>)a(IA-1,0)<<(FQ+1);

  ap_int<ID+1> d_tmp = (ap_int<ID>) d(ID-1,0);

  ap_int<ID+1> r;

  ap_int<ID+1> t;

  ap_uint<IQ+FQ> p;

  ap_fixed<IQ+FQ,IQ> q;

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

    if(i==IQ+FQ-1) {

      r = (ap_int<IA-IQ+2>) a_tmp(IA+FQ,i); //sign-extension

    } else {

      r = (t(ID-1,0),a_tmp[i]);

    }

    p[i] = (r[ID]==d_tmp[ID]);

    t = (p[i])? (r - d_tmp):(r + d_tmp);

  }

  q(IQ+FQ-1,0) = ((ap_uint<IQ+FQ>) 1<<(IQ+FQ-1))

                 + p + (t[ID]==d_tmp[ID]);

  return q;

}

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

(注意) テストベンチを実行するとわかるが、切捨の場合はIQ<IA+1のときa=2IQ-1,d=-1だけe=2-FQになる。例えばIA=3,IQ=2,FQ=0のとき010/(-1)=11+1すなわち2/(-1)=-3+1となる。一方、丸めの場合はもう1桁下まで計算するので2/(-1)=-2+0と正しい結果になる。

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

  const int IA=3;

  const int ID=3;

#ifdef FULL_IQ

  const int IQ=4,FQ=1;  // IQ=IA+1

#else

  const int IQ=2,FQ=1;  // IQ<IA+1

#endif

  ap_int<IA> a;

  ap_int<ID> d;

  ap_fixed<IA+FQ+1,IA+1> q;

  double a_val,d_val,q_val,e_val;

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

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

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

    if(j==0) continue;

    for(int i=-pow(2,IA-1); i<pow(2,IA-1); i++) {

      a = i;

      d = j;

      a_val = a.to_double();

      d_val = d.to_double();

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

      /* IQ<IA+1のとき大きいaはスキップする */

      if( ((d_val>0)&&(-pow(2,IQ-1)*d_val<=a_val)&&(a_val<=(pow(2,IQ-1)-pow(2,-FQ))*d_val))

        ||((d_val<0)&&(-pow(2,IQ-1)*d_val>=a_val)&&(a_val>=(pow(2,IQ-1)-pow(2,-FQ))*d_val))

        ) {

        /* 切捨の場合 */

        q = div_int_trn<IA,ID,IQ,FQ>(a,d);

        q_val = q.to_double();

        e_val = a_val/d_val - q_val;

        if(d_val<0) e_val = -e_val;

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

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

        /* 丸めの場合 */

        q = div_int_rnd<IA,ID,IQ,FQ>(a,d);

        q_val = q.to_double();

        e_val = a_val/d_val - q_val;

        if(d_val<0) e_val = -e_val;

        printf(" q_rnd=%+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;

}

◇◇◇

(補足) IA=3,ID=2,IQ=IA+1=4,FQ=0の場合の計算例

【非回復法】 負/

【非回復法】 負/負 余り0

例)a[2:0]=100=(-4) d[1:0]=11=(-1)

          _______ 

       11)11100.0  r=符号拡張a[2]<0

q[2]=+1 -)111      d

          ---

          000      t=r-d >=0

           000     r={t[1:0],a[1]}

q[1]=-1  +)111     符号拡張d

           ---

           111     t=r+d <0

            110    r={t[1:0],a[0]}

q[0]=+1   -)111    符号拡張d

            ---

            111    t=r-d <0

             11.0  r={t[1:0],0}

q[-1]=+1   -)11.1  符号拡張d

             ---

             11.1  t=r-d

 

切捨の場合q=+4-2+1=3(余り-1)→4

丸めの場合q=+4-2+1+0.5=3.5(余り-0.5)→4

例)a[2:0]=101=(-3) d[1:0]=11=(-1)

          _______ 

       11)11101.0  r=符号拡張a[2]<0

q[2]=+1 -)111      d

          ---

          000      t=r-d >=0

           000     r={t[1:0],a[1]}

q[1]=-1  +)111     符号拡張d

           ---

           111     t=r+d <0

            111    r={t[1:0],a[0]}

q[0]=+1   -)111    符号拡張d

            ---

            000    t=r-d >=0

             00.0  r={t[1:0],0}

q[-1]=-1   +)11.1  符号拡張d

             ---

             11.1  t=r+d

 

切捨の場合q=+4-2+1=3(余り0)

丸めの場合q=+4-2+1-0.5=2.5(余り-0.5)→3

 

【非回復法】 正/

【非回復法】 正/負 余り0

例)a[2:0]=010=( 2) d[1:0]=11=(-1)

          _______ 

       11)00010.0  r=符号拡張a[2]>=0

q[2]=-1 +)111      d

          ---

          111      t=r+d <0

           111     r={t[1:0],a[1]}

q[1]=+1  -)111     符号拡張d

           ---

           000     t=r-d >=0

            000    r={t[1:0],a[0]}

q[0]=-1   +)111    符号拡張d

            ---

            111    t=r+d <0

             11.0  r={t[1:0],0}

q[-1]=+1   -)11.1  符号拡張d

             ----

             11.1  t=r-d

 

切捨の場合q=-4+2-1=-3(余り-1)→-2

丸めの場合q=-4+2-1+0.5=-2.5(余り-0.5)→-2

例)a[2:0]=011=( 3) d[1:0]=11=(-1)

          _______ 

       11)00011.0  r=符号拡張a[2]>=0

q[2]=-1 +)111      d

          ---

          111      t=r+d <0

           111     r={t[1:0],a[1]}

q[1]=+1  -)111     符号拡張d

           ---

           000     t=r-d >=0

            001    r={t[1:0],a[0]}

q[0]=-1   +)111    符号拡張d

            ---

            000    t=r+d >=0

             00.0  r={t[1:0],0}

q[-1]=-1   +)11.1  符号拡張d

             ----

             11.1  t=r+d

 

切捨の場合q=-4+2-1=-3(余り0)

丸めの場合q=-4+2-1-0.5=-3.5(余り-0.5)→-3


[] [] []