您可以使用对数摆脱除法。对于第一象限中的:(x,y)
z=log2(y)−log2(x)atan2(y,x)=atan(y/x)=atan(2z)
图1.atan(2z)
您可能需要在范围内近似才能获得所需的1E-9精度。您可以利用对称或确保在已知的八分圆中。近似:atan(2z)−30<z<30atan(2−z)=π2−atan(2z)(x,y)log2(a)
b=floor(log2(a))c=a2blog2(a)=b+log2(c)
b可以通过找到最高有效非零位的位置来计算。可以通过位移来计算。您将需要在范围近似。clog2(c)1≤c<2
图2.log2(c)
对于你的精度要求,线性内插和均匀采样,的样品和的样品为应该足够了。后者的桌子很大。有了它,由于插值引起的误差在很大程度上取决于:214+1=16385log2(c)30×212+1=122881atan(2z)0<z<30z
图3. 对于每单位间隔的不同数量的样本(32至8192),在不同范围(水平轴近似最大绝对误差。(忽略)的最大绝对误差略小于绝对误差。atan(2z)zz0≤z<1floor(log2(z))=0
所述表可以被分成多个子表对应于和不同与,这是很容易计算。表长度可以选择,如图3所示。子表内索引可以通过简单的位串操作来计算。为了您的准确性要求,如果您将的范围扩展到为简单起见,子表将总共包含29217个样本。atan(2z)0≤z<1floor(log2(z))z≥1atan(2z)z0≤z<32
供以后参考,这是我用来计算近似误差的笨拙的Python脚本:
from numpy import *
from math import *
N = 10
M = 20
x = array(range(N + 1))/double(N) + 1
y = empty(N + 1, double)
for i in range(N + 1):
y[i] = log(x[i], 2)
maxErr = 0
for i in range(N):
for j in range(M):
a = y[i] + (y[i + 1] - y[i])*j/M
if N*M < 1000:
print str((i*M + j)/double(N*M) + 1) + ' ' + str(a)
b = log((i*M + j)/double(N*M) + 1, 2)
err = abs(a - b)
if err > maxErr:
maxErr = err
print maxErr
y2 = empty(N + 1, double)
for i in range(1, N):
y2[i] = -1.0/16.0*y[i-1] + 9.0/8.0*y[i] - 1.0/16.0*y[i+1]
y2[0] = -1.0/16.0*log(-1.0/N + 1, 2) + 9.0/8.0*y[0] - 1.0/16.0*y[1]
y2[N] = -1.0/16.0*y[N-1] + 9.0/8.0*y[N] - 1.0/16.0*log((N+1.0)/N + 1, 2)
maxErr = 0
for i in range(N):
for j in range(M):
a = y2[i] + (y2[i + 1] - y2[i])*j/M
b = log((i*M + j)/double(N*M) + 1, 2)
if N*M < 1000:
print a
err = abs(a - b)
if err > maxErr:
maxErr = err
print maxErr
y2[0] = 15.0/16.0*y[0] + 1.0/8.0*y[1] - 1.0/16.0*y[2]
y2[N] = -1.0/16.0*y[N - 2] + 1.0/8.0*y[N - 1] + 15.0/16.0*y[N]
maxErr = 0
for i in range(N):
for j in range(M):
a = y2[i] + (y2[i + 1] - y2[i])*j/M
b = log((i*M + j)/double(N*M) + 1, 2)
if N*M < 1000:
print str(a) + ' ' + str(b)
err = abs(a - b)
if err > maxErr:
maxErr = err
print maxErr
P = 32
NN = 13
M = 8
for k in range(NN):
N = 2**k
x = array(range(N*P + 1))/double(N)
y = empty((N*P + 1, NN), double)
maxErr = zeros(P)
for i in range(N*P + 1):
y[i] = atan(2**x[i])
for i in range(N*P):
for j in range(M):
a = y[i] + (y[i + 1] - y[i])*j/M
b = atan(2**((i*M + j)/double(N*M)))
err = abs(a - b)
if (i*M + j > 0 and err > maxErr[int(i/N)]):
maxErr[int(i/N)] = err
print N
for i in range(P):
print str(i) + " " + str(maxErr[i])
通过对样本进行线性插值来近似函数的局部最大误差,可以通过采样间隔进行均匀采样而得出,可以通过以下方式近似解析:f(x)˚F(X )˚F (X )Δ Xf^(x)f(x)Δx
fˆ(x)−f(x)≈(Δx)2limΔx→0f(x)+f(x+Δx)2−f(x+Δx2)(Δx)2=(Δx)2f′′(x)8,
其中是的二阶导数,并且处于绝对误差的局部最大值。通过以上方法,我们得到了近似值:f′′(x)f(x)x
atanˆ(2z)−atan(2z)≈(Δz)22z(1−4z)ln(2)28(4z+1)2,log2ˆ(a)−log2(a)≈−(Δa)28a2ln(2).
由于这些函数是凹的,并且样本与该函数匹配,因此误差始终在一个方向上。如果使误差的符号在每个采样间隔内来回交替一次,则局部最大绝对误差可以减半。使用线性插值,可以通过以下方式对每个表进行预过滤来获得接近最佳结果:
y[k]=⎧⎩⎨⎪⎪b2x[k−2]c1x[k−1]+b1x[k−1]b0x[k]+c0x[k]+b0x[k]+b1x[k+1]+c1x[k+1]+b2x[k+2]if k=0,if 0<k<N,if k=N,
其中和是原始表,并且过滤后的表都跨越,权重为。与使用表外函数的样本相比,结束条件(上式中的第一行和最后一行)减少了表末尾的错误,因为不需要调整第一个和最后一个样本以减少内插误差它和桌子外面的样品之间。具有不同采样间隔的子表应单独进行预过滤。权重的值是通过依次最小化以增加指数xy0≤k≤Nc0=98,c1=−116,b0=1516,b1=18,b2=−116c0,c1N 近似误差的最大绝对值:
(Δx)NlimΔx→0(c1f(x−Δx)+c0f(x)+c1f(x+Δx))(1−a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))a−f(x+aΔx)(Δx)N=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪(c0+2c1−1)f(x)01+a−a2−c02(Δx)2f′′(x)if N=0,∣∣∣c1=1−c02if N=1,if N=2,∣∣∣c0=98
对于样本间插值位置,具有凹函数或凸函数(例如)。解决了这些权重后,通过类似地最小化以下项的最大绝对值,得出了最终条件权重值:0≤a<1f(x)f(x)=exb0,b1,b2
(Δx)NlimΔx→0(b0f(x)+b1f(x+Δx)+b2f(x+2Δx))(1−a)+(c1f(x)+c0f(x+Δx)+c1f(x+2Δx))a−f(x+aΔx)(Δx)N=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪(b0+b1+b2−1+a(1−b0−b1−b2))f(x)(a−1)(2b0+b1−2)Δxf′(x)(−12a2+(2316−b0)a+b0−1)(Δx)2f′′(x)if N=0,∣∣∣b2=1−b0−b1if N=1,∣∣∣b1=2−2b0if N=2,∣∣∣b0=1516
为。使用预过滤器可使近似误差减半,并且比对表进行全面优化更容易。0≤a<1
图4. 11个样本的近似误差,带有和不带有预滤波器,带有和不带有端部条件。在没有结束条件的情况下,预过滤器可以访问表外部的函数值。log2(a)
本文可能提出了一种非常相似的算法:R. Gutierrez,V。Torres和J. Valls,“ 基于对数变换和基于LUT的技术的atan(Y / X)的FPGA实现 ” ,《系统结构学报》, 。2010年5月56日。摘要表示,它们的实现在速度方面击败了先前基于CORDIC的算法,在占用空间方面又优于基于LUT的算法。