正規化の除算(非回復法)
ベクトルの内積を正規化するときは商が±1以内に限られるので、通常より少ない引き算(小さい回路)で除算ができる。
-1≦a・b / |a||b|=Σaj bj / sqrt(Σaj2Σbj2)≦+1
分子と分母が整数の場合の除算は正規化の除算(非回復法)に書いたとおりで、これを固定小数点に直したものは次のとおり。余りは切捨でなく丸め(四捨五入)とする。
a / d = q + e 分子a:符号つき、整数部IAビット、小数部FAビット (-2IA-1≦a≦2IA-1-2-FA) 分母d:符号なし、整数部IDビット、小数部FDビット (2-FD≦d≦2ID-2-FD) 商q: 符号つき、整数部2ビット、小数部FQビット (-2≦q≦2-2-FQ) (∵-1≦q≦1の範囲まで求めるためqの整数部は2ビット必要) 誤差e:丸めの場合 -2-FQ-1≦e<2-FQ-1、a,dの範囲は-2≦a/d<(2-2-FQ-1) (∵a/d=q+e<2-2-FQ+2-FQ-1=2-2-FQ-1 ) |
VitisHLSで合成可能なCコードは
#include <ap_int.h> /****************************************************** * division: a/d = q + e * 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+1,ID+1> 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+1>) a(ID+FD,0); // sign-ext } 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; const int FQ=1; ap_fixed<ID+FD+1,ID+1> 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 = (float) i * pow(2,-FD); d = (float) j * pow(2,-FD); a_val = a.to_double(); d_val = d.to_double(); printf("-- a=%+f d=%+f",a_val,d_val); /* 丸めの場合の入力範囲外の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; } |