Vous pouvez utiliser des logarithmes pour vous débarrasser de la division. Pour (x,y) dans le premier quadrant:
z=log2(y)−log2(x)atan2(y,x)=atan(y/x)=atan(2z)
Figure 1. Diagramme d' atan(2z)
Vous auriez besoin d'approximativement un atan(2z) dans la plage −30<z<30 pour obtenir la précision requise de 1E-9. Vous pouvez profiter de la symétrie atan(2−z)=π2−atan(2z)ou bien assurez-vous que(x,y)est dans un octant connu. Pour approximer lelog2(a):
b=floor(log2(a))c=a2blog2(a)=b+log2(c)
b peut être calculé en trouvant l'emplacement du bit non nul le plus significatif. c peut être calculé par un décalage de bits. Vous devez approximerlog2(c) dans la plage1≤c<2 .
Figure 2. Diagramme du log2(c)
214+1=16385log2(c)30×212+1=122881atan(2z)0<z<30z
atan(2z)zz0≤z<1floor(log2(z))=0
atan(2z)0≤z<1floor(log2(z))z≥1atan(2z)z0≤z<32
Pour référence ultérieure, voici le script Python maladroit que j'ai utilisé pour calculer les erreurs d'approximation:
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)Δx
fˆ(x)−f(x)≈(Δx)2limΔx→0f(x)+f(x+Δx)2−f(x+Δx2)(Δx)2=(Δx)2f′′(x)8,
où est la dérivée seconde de et est à un maximum local de l'erreur absolue. Avec ce qui précède, nous obtenons les approximations: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).
Étant donné que les fonctions sont concaves et que les échantillons correspondent à la fonction, l'erreur se produit toujours dans une direction. L'erreur absolue maximale locale pourrait être réduite de moitié si le signe de l'erreur était alterné d'avant en arrière une fois à chaque intervalle d'échantillonnage. Avec une interpolation linéaire, des résultats proches de l'optimisation peuvent être obtenus en préfiltrant chaque table en:
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,
où et sont l'original et la table filtrée s'étendant sur et les poids sont . Le conditionnement final (première et dernière ligne de l'équation ci-dessus) réduit l'erreur aux extrémités du tableau par rapport à l'utilisation d'échantillons de la fonction en dehors du tableau, car le premier et le dernier échantillon n'ont pas besoin d'être ajustés pour réduire l'erreur d'interpolation entre elle et un échantillon juste à l'extérieur de la table. Les sous-tables avec différents intervalles d'échantillonnage doivent être préfiltrées séparément. Les valeurs des poids ont été trouvées en minimisant séquentiellement pour augmenter l'exposantxy0≤k≤Nc0=98,c1=−116,b0=1516,b1=18,b2=−116c0,c1N la valeur absolue maximale de l'erreur approximative:
(Δ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
pour les positions d'interpolation entre échantillons , avec une fonction concave ou convexe (par exemple ). Une fois ces poids résolus, les valeurs des poids de conditionnement d'extrémité ont été trouvées en minimisant de manière similaire la valeur absolue maximale de: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
pour . L'utilisation du préfiltre réduit de moitié l'erreur d'approximation et est plus facile à faire que l'optimisation complète des tableaux.0≤a<1
Figure 4. Erreur d'approximation de partir de 11 échantillons, avec et sans préfiltre et avec et sans conditionnement d'extrémité. Sans conditionnement final, le préfiltre a accès aux valeurs de la fonction juste à l'extérieur du tableau.log2(a)
Cet article présente probablement un algorithme très similaire: R. Gutierrez, V. Torres et J. Valls, « FPGA-implementation of atan (Y / X) based on logarithmic transformation and LUT-based techniques », Journal of Systems Architecture , vol . 56, 2010. Le résumé indique que leur mise en œuvre bat les précédents algorithmes basés sur CORDIC en vitesse et les algorithmes basés sur LUT en taille d'encombrement.