实现正余弦函数的几种算法记录
1、几种算法的理论推导与matlab仿真
(1)Taylor级数逼近正余弦函数的推导
精度要求为20bit,其对应误差为9.536e-7,所以泰勒函数逼近的误差值要小于9.536e-7。
FPGA数据处理时用24bit,数据用Q22格式,计算精度2.38e-7,数据输出20bit。sin(x)与cos(x)的Taylor级数展开为:
(1)
(2)
如果直接使用公式(1)和(2)实现精度较高的正余弦函数,则需要很高的阶数。
通过利用三角函数中的倍角公式可以得到:
(3)
(4)
(5)
如果选择逼近的x的取值范围为[0,π/8],首先可以通过公式(1)和公式(2)计算出此区间上的正余弦值;其次将得到正余弦值再经过倍角公式(3)和(4)就可以得到正余弦函数在区间[0,π/4]的值,如此重复使用两个倍角公式四次就可以得到整个周期中的正余弦函数值。
为了说明这种逼近方式的效果,使用十阶的余弦逼近和九阶的正弦逼近仿真。使用的逼近多项式为公式(6)和(7)。Matlab软件的仿真结果如图1所示。
(6)
(7)
图1正余弦函数的Taylor逼近
由图1可以看出九阶正弦逼近和十阶余弦的逼近计算误差小于3x10^-9!如果直接用taylor级数逼近整个周期中的正余弦值并达到这个精度,则需要28阶的taylor级数逼近。由此可见这种改进的逼近方式比直接用taylor级数直接逼近整个周期的正余弦值所用的级数低了好几倍,计算量也随之降低很多。
通过计算仿真可以知道,每当x的区间降低一半,倍角公式增加一级,计算精度将提高近10倍。因此在实际计算得到20bit的精度是使用三阶逼近,x的取值区间为[0:π/32],使用八级倍角实现整个周期内的正余弦函数计算。
图 2 级数逼近算法仿真图
(2)Cordic算法实现正余弦函数
Cordic(Coordinate Rotation Digital Computer)算法主要应用于超越函数的近似计算。主要包括直角坐标系,柱坐标系和球面坐标系三种坐标下的计算。这里只讨论直角坐标系下的正余弦函数的实现。
图3 cordic算法直角坐标图
在如图所示的坐标系中,从A(x1,y1)点逆时针旋转θ角度到达B(x2,y2)点。如果OA与x轴的夹角为φ,L1,L2分别为A点、B点到原点的长度,则:
(11)
(12)
由方程式(11)和(12)可得到如下的方程式。
(13)
其中k = L2/L1 ,称为伸缩因子。当k为常数时,旋转的点都在同一个圆上,取k=1,方程式(13)的矩阵表达式如下:
(14)
经过n次旋转后可得到如下的公式。
(15)
其中,Si为每次旋转的方向,若为逆时针旋转Si=1,否则Si=-1。
如果tan(θi) = 2-i,则可以得到如下的方程式。
(16)
(17)
所以方程式(15)可改写成如下的表达式。
(18)
当n趋于无群大时,K = 1.64676024187。为了计算某一角度的正余弦值,需要一个变量来存储角度的累加。令Z为角度累加器,则累加第i次的角度表达式如下。
(19)
其中当Zi大于零时Si为1,否则Si为-1。
因为θi=arctan2-i,所以每次旋转的角度是固定的,例如首次旋转角度为45°,第二次旋转角度为26.6°以此类推。角度累加器的范围近似为-99.7°到99.7°。当x1=1,y1=0,Z1=β时,最终所求得的Xn+1=cos(β),Yn+1=sin(β) 。
Cordic算法的精度主要取决于旋转的次数n,计算精度为2-n+1。显然当n越大误差越小,精度越高。利用Matlab仿真23阶Cordic算法的结果如图4所示。
图4 23阶Cordic算法仿真结果
(3)查表插值法
查表插值法是将正余弦函数一个周期内的n个值按一定的精度存储在一个可查询的表中,产生n-1个区间,查询输入变量落入n-1个区间的哪个的区间,通过落入区间内的插值方法求出满足精度要求的变量的正余弦函数值。
定义xϵ[-π,π],将此区间均分为n份,存储表table中将有n+1个可查询的正(余)值,每一个表中的数据和[-π,π]上的特定的值对应,且每个间隔为d=2π/n,可通过如下的公式计算x落入的区间。
(20)
对D1取其整数部分即可得到x落入的区间D,D1的小数DP部分可用于线性插值的变量。线性插值的公式如下。
(21)
如此可以得到的f(x)就是x的正(余)弦查表插值结果。
查表插值算法的精度主要取决于存储表中的位宽,其精度又受到插值算法和存储个数的影响。存储位宽越大精度就高,存储数据个数越多,相邻两个值之间的间距越小,插值的误差就小,精度就越高。最终的精度由插值的算法决定。好的插值算法其精度就越接近存储数据位宽的精度,如果插值算法不好,整体精度将远低于存储位宽的精度。线性插值法比较简单且易实现。
在实现20位二进制的精度时,所选取的位宽和存储数据的个数共同决定着插值算法的精度大小。存储位宽必须大于20位,存储位宽和存储个数的乘积决定了存储器的大小。表1是实现精度为20位时选取的存储位宽和存储个数的关系。
表2相同精度下存储位宽与存储个数的关系
存储位宽
20
21
22
23
24
25
26
存储点数
>=7000
>=2900
>=2400
>=2300
>=2200
>=2200
>=2200
存储器消耗(bit)
140K
60.9K
52.8K
52.9K
52.8K
55K
57.2K
由表2可以看出,当选择22位或者24位存储位宽是所用的存储单元消耗可以达到最少。因此,在matlab中仿真时所用的存储位宽为22位,存储个数为2400,占用存储单元53.8Kbit。查表插值算法实现正余弦算法在Matlab中的仿真结果如 图5所示。
图520bit线性插值仿真图
2、几种算法实现正余弦函数的复杂度对比(一个周期内的点数为n)
Taylor 级数的算法中高阶指数的乘法消耗了主要运算量,因此其算法复杂度主要体现在乘加运算上。如果逼近的最高阶数为N,所取区间的产度为a,则Taylor级数逼近的运算中乘法次数为:2N+2log2(2π/a) - 1,加法次数为:N - 1 + log2(2π/a)。 例如用七阶逼近且取值区间为[0,π/32]经过化简后的乘法组合后,计算一次个正余弦函数值需要要是25次乘法和12次加法运算。用线性Cordic算法的复杂度主要消耗在移位累加,每级算法需要三次加法和两次移位运算,如果使用m级实现精度为2^(-m+1)的计算中需要使用3m次加法和2m次移位。采用线性查表插值的复杂度主要体现在查表后的乘加运算量上,计算一个点的正余弦函数值需要两次乘法、四次次加法(减法计入加法)。
表1 四种算法的复杂度
Taylor级数逼近
Cordic算法(m级)
查表插值
复杂度
(乘加运算次数)
33n
3mn次加法2mn次移位
2n次乘法和4n次加法
精度为16bit时
输入范围[0,π/32],
后级乘减3级
m=19,复杂度为
76n
存储数据个数大于248(数据位宽16)
精度为20bit时
输入范围[0,π/32],
后级乘减5级,
m= 23 ,复杂度为
92n
存储数据个数大于2662(数据位宽20)
硬件实现
资源消耗状况
乘法器和加法器,
无需存储器
只需加法器和移位寄存器,无乘法器,无存储器
乘法器、加法器和存储器均需要,且存储消耗大