Puoi usare i logaritmi per sbarazzarti della divisione. Per nel primo quadrante:(x,y)
z=log2(y)−log2(x)atan2(y,x)=atan(y/x)=atan(2z)
Figura 1. Grafico diatan(2z)
Dovresti approssimare nell'intervallo per ottenere la precisione richiesta di 1E-9. Puoi sfruttare la simmetria o in alternativa assicurarti che è in un ottante noto. Per approssimare :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 può essere calcolato trovando la posizione del bit diverso da zero più significativo. può essere calcolato con un bit shift. Dovresti approssimare nell'intervallo .clog2(c)1≤c<2
Figura 2. Grafico dilog2(c)
Per i requisiti di precisione, interpolazione lineare e campionamento uniforme, campioni di e campioni di per dovrebbe essere sufficiente. Quest'ultimo tavolo è piuttosto grande. Con esso, l'errore dovuto all'interpolazione dipende molto da :214+1=16385log2(c)30×212+1=122881atan(2z)0<z<30z
Figura 3. Errore assoluto maggiore approssimazione approssimazione per diversi intervalli di (asse orizzontale) per diversi numeri di campioni (da 32 a 8192) per intervallo di unità di . L'errore assoluto più grande per (omesso) è leggermente inferiore rispetto a .atan(2z)zz0≤z<1floor(log2(z))=0
La tabella può essere suddivisa in più sottotitoli che corrispondono a e diversi con , che è facile calcolare. Le lunghezze della tabella possono essere scelte come guidato dalla Fig. 3. L'indice all'interno della sottotabella può essere calcolato con una semplice manipolazione della stringa di bit. Per i requisiti di precisione, i avranno un totale di 29217 campioni se si estende l'intervallo di a per semplicità.atan(2z)0≤z<1floor(log2(z))z≥1atan(2z)z0≤z<32
Per riferimento futuro, ecco il grosso script di Python che ho usato per calcolare gli errori di approssimazione:
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])
L'errore massimo locale derivante dall'approssimazione di una funzione mediante l'interpolazione lineare di da campioni di , prelevati mediante campionamento uniforme con intervallo di campionamento , può essere approssimato analiticamente mediante: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,
dove è la seconda derivata di e è al massimo locale dell'errore assoluto. Con quanto sopra otteniamo le approssimazioni: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).
Poiché le funzioni sono concava e i campioni corrispondono alla funzione, l'errore è sempre in una direzione. L'errore assoluto massimo locale potrebbe essere dimezzato se il segno dell'errore fosse fatto alternare avanti e indietro una volta ogni intervallo di campionamento. Con l'interpolazione lineare, è possibile raggiungere risultati ottimali prefiltrando ogni tabella mediante:
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,
dove ed sono l'originale e la tabella filtrata sia attraversa ei pesi sono . Il condizionamento finale (prima e ultima riga dell'equazione precedente) riduce l'errore alle estremità della tabella rispetto all'utilizzo di campioni della funzione all'esterno della tabella, poiché non è necessario regolare il primo e l'ultimo campione per ridurre l'errore di interpolazione tra esso e un campione appena fuori dal tavolo. Sottotitoli con intervalli di campionamento diversi devono essere pre-filtrati separatamente. I valori dei pesi sono stati trovati minimizzando in sequenza per aumentare l'esponentexy0≤k≤Nc0=98,c1=−116,b0=1516,b1=18,b2=−116c0,c1N il valore assoluto massimo dell'errore approssimativo:
(Δ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
per le posizioni di interpolazione tra campioni , con una funzione concava o convessa (ad esempio ). i pesi, i valori dei pesi di condizionamento finale sono stati trovati minimizzando allo stesso modo il valore assoluto massimo di: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
per . L'uso del prefiltro dimezza l'errore di approssimazione ed è più semplice da eseguire rispetto all'ottimizzazione completa delle tabelle.0≤a<1
Figura 4. Errore di approssimazione di da 11 campioni, con e senza prefiltro e con e senza condizionamento finale. Senza il condizionamento finale il prefiltro ha accesso ai valori della funzione appena fuori dalla tabella.log2(a)
Questo articolo presenta probabilmente un algoritmo molto simile: R. Gutierrez, V. Torres e J. Valls, " Implementazione FPGA di atan (Y / X) basata sulla trasformazione logaritmica e tecniche basate su LUT " , Journal of Systems Architecture , vol . 56, 2010. L'abstract afferma che la loro implementazione supera i precedenti algoritmi basati su CORDIC in termini di velocità e algoritmi basati su LUT in termini di dimensioni dell'impronta.