You are on page 1of 6

El algoritmo CORDIC

ltimos Cambios

Mi estado actual en Jabber/XMPP:

- jabberES - jabber.org

El algoritmo CORDIC
ltima Actualizacin: 26 de Febrero de 2003 - Mircoles Tradicionalmente el clculo de funciones transcendentales y trigonomtricas se ha realizado mediante expansiones polinmicas, como polinomos de Taylor, Min-Max o Chevichev. Dejando a un lado sus caractersticas de convergencia y error, la evaluacin de polinomios, aunque se simplifique mediante la regla de Horner, requiere el uso de multiplicaciones. En la actualidad, las CPUs modernas disponen de instrucciones de multiplicacin muy rpidas, pero no es el caso en entornos empotrados o en desarrollos ASIC. El algoritmo CORDIC (Coordinate Rotation Digital Computer), propuesto en 1959, permite el clculo de este tipo de funciones de una forma muy simple, rpida y precisa, sin requerir hardware caro como una unidad de multiplicacin. La formulacin normal de CORDIC, de forma genrica, es:
x[n+1]=x[n]-m*s[n]*y[n]*2 -n y[n+1]=y[n]+s[n]*x[n]*2 -n z[n+1]=z[n]-s[n]*e[n]

Donde m es 0, +1 o -1, s[n] es +1 o -1, y e[n] son constantes precalculadas.

Clculo de senos y cosenos


Supongamos un vector bidimensional, con origen (0,0) y extremo (x,y) . Si giramos el vector un ngulo A, respecto al origen (0,0) , su nuevo extremo ser (x',y')=(x*cos(A)-y*sin(A),x*sin(A)+y*cos(A)). Este nuevo vector, por supuesto, puede rotarse un ngulo adicional aplicando la frmula las veces que sea necesario. Si partimos de un vector (1,0) y lo giramos un ngulo A para obtener un vector (x,y) , sabemos por trigonometra bsica que cos(A)=x y sin(A)=y . Es decir no solo obtenemos senos y cosenos, sino que los podemos obtener simultaneamente. Pero la ecuacin propuesta no solo requiere multiplicaciones, sino que requiere el clculo de esos senos y cosenos que son lo que queremos obtener. Dnde est el truco?. Partiendo de la ecuacin, sabemos que para rotar un vector un ngulo A, tenemos, de forma iterativa: (x[n+1],y[n+1])=(x[n]*cos(A[n])y[n]*sin(A[n]),x[n]*sin(A[n])+y[n]*cos(A[n])) , donde (x[n],y[n]) es la serie de vectores que vamos obteniendo, y A[n] es la serie de ngulos que vamos aplicando a cada vector. Si sacamos el coseno como factor comn, tenemos (x[n+1],y[n+1])=cos(A[n])*(x[n]-y[n]*tan(A[n]),x[n]*tan(A[n])+y[n])). El ngulo final A ser la suma de ngulos A[n] , aplicados etapa a etapa. Los valores concretos de cada A[n] son irrelevantes, y los podemos elegir como queramos, siempre que cubran el rango y la precisin que necesitamos. Tanto es as que podemos elegir ngulos A[n] que cumplan una propiedad que es la "idea feliz" que hace posible CORDIC. Supongamos que elegimos los A[n] de forma que tan(A[n])=2-n. Con esto, el clculo de la tangente desaparece, y la multiplicacin con ella se reduce a un desplazamiento de bits, que es una operacin tremendamente eficiente. Completando las iteraciones y sacando factor comn, obtenemos:
(cos(A),sin(A)) = (x,y) = Producto(cos(A[n]))*Iteracin(x[n]-y[n]>>n),x[n]>>n+y[n]) . El vector inicial ser (1,0) , que se

corresponde a un ngulo de cero grados. A la hora de calcular el ngulo A a partir de los A[n] existen dos opciones bsicas. Supongamos que S[n] es el ngulo que llevamos rotado hasta el momento n. La primera opcin es

http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

El algoritmo CORDIC

S[n+1]=S[n]+A[n] si S[n+1]< kbd="">, y S[n+1]=S[n] en caso contrario. Esta opcin es muy sencilla de implementar, pero tiene el problema de que Producto(cos(A[n])) no es una constante, sino que depende de los A[n] concretos que se utilicen. Ni que decir que esto es una faena. <>

La segunda opcin consiste en que S[n+1]=S[n]+M[n]*A[n], donde M[n] puede ser el valor +1 o -1. La eleccin de uno u otro puede hacerse comparando el error respecto al ngulo final A. Otra opcin simple es usar +1 si estamos por debajo del ngulo final, y -1 si estamos por encima. Dado que cos(A)=cos(-A), con esta eleccin Producto(cos(A[n])) es una constante independiente del ngulo final a obtener. Slo depende de la eleccin de los A[n] . Valores que, por otra parte, estn ya elegidos. Veamos un ejemplo en lenguaje Python (escrito para que se entienda, no para que sea eficiente):
class cordic : def __init__(self) : import math angulos=[] c=1.0 p=1.0 for i in xrange(16) : a=math.atan(p) c*=math.cos(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def sincos(self,angulo) : x,y=(1.0,0.0) p=1.0 a=0 for i in self.angulos : if a<angulo : (x,y)=(x-y/p,y+x/p) a+=i else : (x,y)=(x+y/p,y-x/p) a-=i p*=2 return (y*self.c,x*self.c)

En el constructor se crean las tablas que vamos a necesitar. En una implementacin hardware, esas tables estara ya integradas en el diseo electrnico del sistema. El mtodo sincos() nos devuelve, simultaneamente, el seno y el coseno del ngulo solicitado. La precisin depende de: 1. El nmero de etapas (definicin de ngulos) utilizados. 2. Precisin de la tabla de tangentes. 3. Precisin de los resultados intermedios. Cuando se implementa por software en una CPU moderna, el mayor problema es el tercer punto, ya que la mayora de los algoritmos de clculo de seno y coseno (en particular IEEE-754) garantizan un error bajo (con un gran coste computacional, eso s). En ese sentido una implementacin CORDIC no puede competir en trminos de precisin, aunque el resultado es sorprendentemente bueno. En mi sistema, la clase Python anterior tiene una precisin de cinco dgitos decimales, en el peor de los casos. Esta precisin se puede incrementar siendo consciente de las prdidas de precisin de los nmeros en coma flotante cuando se restan nmeros, por ejemplo, de rdenes de magnitud muy diferentes. Pero ste es otro tema. Si la implementacin es hardware, el algoritmo CORDIC arrasa. Este algoritmo se puede usar para rotar vectores arbitrarios, convertir coordenadas entre notacin polar y cartesiana (en ambos sentidos) y obtener la funciones trigonomtricas inversas (dado un coseno, por ejemplo, obtener su ngulo). Modificndolo de forma obvia (y haciendo m=-1, se puede usar la misma tcnica para trabajar con trigonometra hiperblica.

Arcotangente y magnitud
Si en la frmula cannica de CORDIC hacemos m=1 , e[n]=tan -1(2-n), s[n]=-sign(y[n]) , z[0]=0, obtenemos que:
z[n+1]=tan -1 (y[0]/x[0])

y
x[n+1]=sqrt(x[0]2 +y[0] 2 )/K , donde K es el producto de

los cosenos de e[n] .

http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

El algoritmo CORDIC

La rutina en Python sera:


class cordic : def __init__(self) : import math angulos=[] c=1.0 p=1.0 for i in xrange(16) : a=math.atan(p) c*=math.cos(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def tan1mag(self,x,y) : z=0.0 p=1.0 for i in self.angulos : if y>=0 : x,y,z=x+y/p,y-x/p,z+i else : x,y,z=x-y/p,y+x/p,z-i p*=2 return z,x*self.c

Seno y coseno hiperblicos


Si en la frmula cannica de CORDIC hacemos m=-1, e[n]=tanh -1(2-n), s[n]=sign(z[n]) , y[0]=0, x[0]=1 y z[0]=A, obtenemos el seno y coseno hiperblicos del ngulo A. Para asegurar la convergencia, es necesario repetir algunos valores de e[n] . En concreto hace falta repetir los ndices de la forma 4, 13, 39, k, 3*k+1,... La rutina en Python sera:
class cordic : def __init__(self) : import math,cmath angulos=[] c=1.0 p=0.5 for i in xrange(16) : a=cmath.atanh(p).real c*=math.cosh(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def sincosh(self,a) : import math x=1.0 y=0.0 z=float(a) p=2 j=1 d=4 c=self.c for i in self.angulos : if z<0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i if d==j : c*=math.cosh(i) d=3*d+1 if z<0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i j+=1 p*=2 return y*c,x*c

Arcotangente hiperblica y raz cuadrada de la resta de cuadrados


Si en la frmula cannica de CORDIC hacemos m=-1, e[n]=tanh -1(2-n), s[n]=-sign(y[n]) , z[0]=0, obtenemos que:
z[n+1]=tanh-1 (y[0]/x[0])

y
x[n+1]=sqrt(x[0]2 -y[0] 2 )/K , donde K es el producto de

los cosenos hiperblicos de e[n] .

Para asegurar la convergencia, es necesario repetir algunos valores de e[n] . En concreto hace falta repetir los ndices de la forma 4, 13, 39, k, 3*k+1,...

http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

El algoritmo CORDIC

La rutina en Python sera:


class cordic : def __init__(self) : import math,cmath angulos=[] c=1.0 p=0.5 for i in xrange(16) : a=cmath.atanh(p).real c*=math.cosh(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def atanhmagneg(self,x,y) : import math x=float(x) y=float(y) z=0.0 p=2 j=1 d=4 c=self.c for i in self.angulos : if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i if d==j : c*=math.cosh(i) d=3*d+1 if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i j+=1 p*=2 return z,x*c

Logaritmos neperianos
Si en la frmula cannica de CORDIC hacemos m=-1, e[n]=tanh -1(2-n), s[n]=-sign(y[n]) , x[0]=w+1 , y[0]=w-1 y z[0]=0, obtenemos el logaritmo neperiano del valor w. Para asegurar la convergencia, es necesario repetir algunos valores de e[n] . En concreto hace falta repetir los ndices de la forma 4, 13, 39, k, 3*k+1,... La rutina en Python sera:
class cordic : def __init__(self) : import math,cmath angulos=[] c=1.0 p=0.5 for i in xrange(16) : a=cmath.atanh(p).real c*=math.cosh(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def log(self,w) : import math x=float(w)+1 y=float(w)-1 z=0.0 p=2 j=1 d=4 c=self.c for i in self.angulos : if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i if d==j : c*=math.cosh(i) d=3*d+1 if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i j+=1 p*=2 return 2*z

La velocidad de convergencia (error y nmero de pasos) depende del valor concreto a calcular. Llegado el caso, resulta muy conveniente realizar preescalados para aplicar el algoritmo en los rangos ms favorables.

Raz cuadrada
http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

El algoritmo CORDIC

Tradicionalmente las races cuadradras se calculan como sqrt(x)=exp(log(x)/2). Por supuesto, esta opcin es tremendamente lenta. Se puede implementar un sistema mucho ms eficiente, aunque requiere multiplicaciones.
def sqrt(valor) : base=256.0 y=yy=0.0 for i in xrange(16) : yy=y+base if yy*yy <= valor : y=yy base/=2.0 return y

Esta rutina obtiene la raz cuadrada de un nmero inferior a 65536. El grado de precisin es prcticamente ilimitado, y depende de la precisin de los clculos intermedios y del nmero de iteraciones que elijamos. Hay que tener mucho cuidado con el tema de la precisin intermedia de los resultados en coma flotante. Si tenemos valores muy grandes o muy pequeos, se usa un escalado, y solucionado. Por ejemplo, si queremos sacar la raz cuadrada de 1224744871, lo dividimos primero por 65536, calculamos la raz cuadrada de ese valor y multiplicamos el resultado por 256. El algoritmo CORDIC no requiere multiplicaciones. Si hacemos m=-1, e[n]=tanh -1(2-n), s[n]=-sign(y[n]) , x[0]=w+0.25, y[0]=w-0.25 y z[0]=0, obtenemos la raz cuadrada de w. Para asegurar la convergencia, es necesario repetir algunos valores de e[n] . En concreto hace falta repetir los ndices de la forma 4, 13, 39, k, 3*k+1,... La rutina en Python sera:
class cordic : def __init__(self) : import math,cmath angulos=[] c=1.0 p=0.5 for i in xrange(16) : a=cmath.atanh(p).real c*=math.cosh(a) p/=2.0 angulos.append(a) self.c=c self.angulos=angulos def sqrt(self,w) : import math x=float(w)+0.25 y=float(w)-0.25 z=0.0 p=2 j=1 d=4 c=self.c for i in self.angulos : if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i if d==j : c*=math.cosh(i) d=3*d+1 if y>=0 : x,y,z=x-y/p,y-x/p,z+i else : x,y,z=x+y/p,y+x/p,z-i j+=1 p*=2 return x*c

La velocidad de convergencia (error y nmero de pasos) depende del valor concreto a calcular. Llegado el caso, resulta muy conveniente realizar preescalados para aplicar el algoritmo en los rangos ms favorables.

Inversa de un nmero
La rutina en Python sera:
def inverso(n) : a=1 r=0 for i in xrange(16) : r+=r if a>=n : r+=1 a-=n a+=a if a>=n : r+=1 return r/32768.0

http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

El algoritmo CORDIC

Multiplicaciones y divisiones
Aunque la mayora de las CPUs modernas incluyen estas funciones como primitivas relativamente rpidas (la divisin suele seguir siendo bastante lenta comparada con el resto de operaciones), en entornos integrados o en CPUs pequeas (PIC, microcontroladores) se requiere un sistema simple y eficiente. Podemos tener y[n+1]=x[0]*z[0] haciendo, en la frmula cannica de CORDIC, m=0 , e[n]=2-n, s[n]=sign(z[n]) , y[0]=0. Para obtener z[n+1]=y[0]/x[0], hacemos m=0 , e[n]=2-n, s[n]=-sign(y[n]) , z[0]=0. Los algoritmos resultantes son muy similares a los normales de lpiz y papel.

Consideraciones finales
1. En muchos casos la convergencia y el error dependen del intervalo utilizado para los clculos. En esos casos es conveniente utilizar tcnicas de preescalado/postescalado. 2. En algunos listados propuestos aparecen multiplicaciones o divisiones como paso final antes de devolver el resultado. En muchos casos ese escalado por una constante se puede integrar en la inicializacin de los vectores. Por ejemplo, a la hora de calcular el seno y el coseno, podemos obviar las dos multiplicaciones finales si en vez de inicializar el sistema con el vector (1,0) , se inicializa con el vector (self.c,0) . 3. Invirtiendo el movimiento de los vectores en el algoritmo para calcular logaritmos neperianos, obtendremos exponenciaciones en base e. Lo dejo como ejercicio al lector };-) Artculos sobre Algoritmos Artculos La Pgina de Jess Cea Avin 2003 jcea@jcea.es

http://www.jcea.es/artic/cordic.htm[12/10/2012 02:01:51 p.m.]

You might also like