Bạn có thể sử dụng logarit để thoát khỏi sự phân chia. Với (x,y) trong góc phần tư thứ nhất:
z=log2(y)−log2(x)atan2(y,x)=atan(y/x)=atan(2z)
Hình 1. Lô đất của atan(2z)
Bạn sẽ cần xấp xỉ atan(2z) trong phạm vi −30<z<30 để có độ chính xác cần thiết là 1E-9. Bạn có thể tận dụng atan đối xứng ( 2 - z ) = πatan(2−z)=π2−atan(2z)hoặc thay thế đảm bảo rằng(x,y)nằm trong một quãng tám đã biết. Để tính gần đúnglog2(a):
b=floor(log2(a))c=a2blog2(a)=b+log2(c)
b có thể được tính bằng cách tìm vị trí của bit khác không có ý nghĩa nhất. c có thể được tính bằng một sự thay đổi bit. Bạn sẽ cần xấp xỉlog2(c) trong phạm vi1≤c<2 .
Hình 2. Lô của log2(c)
Đối với các yêu cầu chính xác của bạn, nội suy tuyến tính và lấy mẫu thống nhất, 214+1=16385 mẫu của log2(c) và 30×212+1=122881 mẫu atan(2z) cho 0<z<30 đủ. Bảng sau khá lớn. Với nó, lỗi do nội suy phụ thuộc rất lớn vào z :
Hình 3. Lỗi tuyệt đối lớn nhất xấp xỉ atan(2z) cho các phạm vi khác nhau của z (trục hoành) cho các số lượng mẫu khác nhau (32 đến 8192) trên mỗi đơn vị khoảng z . Lỗi tuyệt đối lớn nhất cho 0≤z<1 (bỏ qua) ít hơn một chút so với floor(log2(z))=0 .
Bảng atan(2z) có thể được chia thành nhiều phụ đề tương ứng với 0≤z<1 và các floor(log2(z)) khác nhau ( log 2 ( z ) ) với z≥1 , rất dễ tính toán. Độ dài bảng có thể được chọn theo hướng dẫn trong Hình 3. Chỉ số bên trong có thể được tính bằng thao tác chuỗi bit đơn giản. Đối với yêu cầu độ chính xác của bạn, các phụ đề atan(2z) sẽ có tổng số 29217 mẫu nếu bạn mở rộng phạm vi từ z đến 0≤z<32 vì đơn giản.
Để tham khảo sau, đây là tập lệnh Python cồng kềnh mà tôi đã sử dụng để tính toán các lỗi gần đúng:
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])
Các lỗi tối đa địa phương từ xấp xỉ một hàm f(x) bằng tuyến tính nội suy f ( x ) từ các mẫu của f ( x ) , lấy bằng cách lấy mẫu thống nhất với lấy mẫu khoảng Δ x , có thể xấp xỉ phân tích theo: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,
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).
Bởi vì các hàm là lõm và các mẫu khớp với hàm, nên lỗi luôn luôn theo một hướng. Lỗi tuyệt đối tối đa cục bộ có thể giảm đi một nửa nếu dấu hiệu lỗi được thực hiện để thay thế qua lại một lần sau mỗi khoảng thời gian lấy mẫu. Với phép nội suy tuyến tính, gần với kết quả tối ưu có thể đạt được bằng cách lọc trước mỗi bảng bằng cách:
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
log2(a)
Bài viết này có thể trình bày một thuật toán rất giống nhau: R. Gutierrez, V. Torres, và J. Valls, “ nghệ FPGA thực hiện atan (Y / X) dựa trên sự biến đổi logarit và kỹ thuật LUT-based, ” Journal of Systems Kiến trúc , vol . 56, 2010. Bản tóm tắt cho biết việc triển khai của họ đánh bại các thuật toán dựa trên CORDIC trước đây về tốc độ và thuật toán dựa trên LUT ở kích thước dấu chân.