Rappels de trigonométrie.
Le cercle de rayon \(1\) centré en l'origine \(O:=(0,0)\) du plan euclidien est appelé cercle trigonométrique. Soit \({\color{yellow}M}\) un point de ce cercle. On note respectivement \(\color{yellow}{C}\) et \(\color{yellow}{S}\) les projections orthogonales de \(M\) sur l'axe des abscisses et sur l'axe des ordonnées et \(\theta\) l'angle \(\widehat{COM}\) qui est défini en première approche comme la mesure de l'arc* entre le point \(I:=(1,0)\) et le point \(M:=(x,y)\)*La mesure du cercle entier est donc \(2\pi\) radians. Parfois nous utiliserons le degré comme unité de mesure des angles avec \(2\pi\equiv 360°\)..
L'abscisse \(x\) et l'ordonnée \(y\) du point \(M\) définissent respectivement le cosinus et le sinus de l'angle \(\theta\), le ratio \(y/x\) définit sa tangente : \begin{equation}\label{eq:deftan} \cos \theta:=x=\overline{OC}\qquad \sin \theta:=y=\overline{OS} \qquad \tan \theta:=\frac{y}{x}=\frac{\sin \theta}{\cos \theta} \end{equation}
Par construction, le triangle \(OCM\) est rectangle en \(C\) et le théorème de Pythagore fournit un premier résultat : \begin{equation}\label{eq:pythagore} \def\R{{\mathbb R}} \def\Z{{\mathbb Z}} \cos^2\theta+\sin^2\theta=1. \end{equation}
Le domaine de définition des fonctions cosinus et sinus est étendu à l'ensemble des réels \(\R\), l'angle étant calculé modulo \(2\pi\) et \(\cos\theta\in[-1,1]\), \(\sin\theta\in[-1,1]\). La fonction cosinus s'annule en tout angle \(\theta=k\pi+\frac{\pi}{2}\) où \(k\in\Z\), le domaine de définition de la fonction tangente est donc \(\R\setminus\frac{\pi}{2}+\pi\Z\) et elle est à valeur dans \(\R\). Les fonctions réciproques sont notées \(\arccos\), \(\arcsin\) et \(\arctan\).
On ne représente ci-dessous que la part du cercle trigonométrique dans le premier quadrant et l'angle \(\theta\) est mesuré en degrés pour plus de simplicité :
On rappelle quelques formules utiles : \begin{align*} \cos(a+b)&=\cos a\cos b-\sin a\sin b & \cos a\cos b&=\frac{1}{2}\left(\cos(a-b)+\cos(a+b)\right)\\ \sin(a+b)&=\sin a\cos b+\cos a\sin b &\sin a\sin b&=\frac{1}{2}\left(\cos(a-b)-\cos(a+b)\right) \\ \cos a+\cos b&=2\cos\frac{a+b}{2}\cos\frac{a-b}{2} & \sin a+\sin b&=2\sin\frac{a+b}{2}\cos\frac{a-b}{2}\\ \end{align*}
Étude du problème. Rotations.
Pour un angle donné \(\theta\), on cherche à calculer efficacement \(\sin\theta\), \(\cos\theta\) et \(\tan\theta\). Une première idée est d'utiliser un développement limité de ces fonctions à l'angle \(\theta\) considéré, mais cette approche générique n'exploite pas les spécificités des fonctions trigonométriques.
L'algorithme CORDIC — acronyme de COordinate Rotation DIgital Computer — que nous présenterons à la section suivante est plus efficace. Il a été développé par Jack Volder en 1956 et consiste à approcher le point \(M=(\cos\theta,\sin\theta)\) par un point \(N\) du cercle unité initialement placé en \(I=(1,0)\) et qui va subir une succession de rotations d'angles de plus en plus petits. À la manière de la dichotomie, le sens de la rotation dépend de la position du point \(N\) par rapport à \(M\) sur le cercle. Elle s'effectue dans le sens trigonométrique si \(N\) est en-dessous de \(M\), dans le sens horaire dans le cas contraire. La première rotation se fait donc dans le sens trigonométrique puisque \(N\) commence son périple du point \(I=(1,0).\)
On désigne par \(\gamma_i\) l'angle de la \((i+1)\)-ème rotation, l'indexation débutant à 0 pour ne pas alourdir inutilement les futures expressions. Ces angles étant supposés positifs, le sens de la rotation \(i\) est noté \(s_i=\pm 1\), il est positif dans le sens trigonométrique, négatif sinon. On note \((N_n)_{n\in{\mathbf N}}\) la suite des points obtenus après**Le point \(N_0\) n'est donc pas le point \(I\). la \((n+1)\)-ème rotation (d'indice \(n\)) et \(\theta_n\) l'angle formé par le point \(N_n\). Par construction \begin{equation} \forall n\in{\mathbf N},\quad \theta_n=\sum_{i=0}^{n}s_i\gamma_i. \end{equation}
Après \(n+1\) rotations, les coordonnées \(N_n=(\cos\theta_n,\sin\theta_n)\) du point \(N\) fournissent donc une approximation de \(\cos\theta\) et \(\sin\theta\). Une rotation s'exprime formellement à l'aide d'une application linéaire du corps des complexes \(\mathbf{C}\) dans lui même :
Nous supposerons que \(\theta\in[0,\frac{\pi}{2}]\) puisque l'on peut déduire les cosinus et sinus pour les autres valeurs de \(\theta\) à l'aide des symétries usuelles, cf. exercice suivant.
On note \(R_i\) la matrice de rotation d'angle \(s_i\gamma_i\). Nous allons réécrire l'expression \((\ref{eq:rotation})\) afin d'optimiser les calculs. On factorise par \(\cos\gamma_i\) :
\begin{equation}\label{eq:cosfact} R_i=\cos\gamma_i\begin{pmatrix} 1& -s_i\tan\gamma_i\\ s_i\tan\gamma_i & 1 \end{pmatrix}. \end{equation}Pour que ce processus d'approximation de l'angle \(\theta\) soit valide avec cette succession de rotations d'angles décroissants prédéfinis \((\gamma_n)_{n\in{\mathbf N}}\), il faut qu'à chaque étape l'angle restant à combler entre \(N\) et \(M\) soit inférieur à l'angle de la rotation qui vient d'être réalisée, il est donc nécessaire que les inégalités suivantes soient satisfaites :
\begin{equation}\label{eq:approxtheta} \forall n\in{\mathbf N},\quad\Big|\theta-\theta_n\Big|\leq\gamma_{n}. \end{equation}La suite \((\gamma_n)_{n\in{\mathbf N}}\) doit évidemment converger vers \(0\) puisque l'objectif est d'approcher \(\theta\). Cependant la convergence de la série \(\theta_n\) n'est pas nécessairement possible pour n'importe quelle valeur de \(\theta\). Les angles \(\gamma_i\) étant tous positifs, il est clair que les valeurs limites de la série \(\theta_n\) sont obtenues avec des signes \(s_i\) tous égaux à \(-1\) ou \(+1\), ce qui impose pour \(\theta\) que :
\begin{equation}\label{ineq:encadrementtheta} -\lim_{n\rightarrow+\infty}\sum_{i=0}^{n}{\gamma_i}\leq\theta\leq \lim_{n\rightarrow+\infty}\sum_{i=0}^{n}{\gamma_i}. \end{equation}Revenons à l'inégalité \((\ref{eq:approxtheta})\). Supposons qu'elle soit satisfaite au rang \(n\), observons ce que celà entraîne pour le rang suivant. Nous avons vu que le sens \(s_i\) de la rotation \(R_i\) est déterminé par la position du point \(N\) par rapport au point \(M\) sur le cercle unité. Il faut donc distinguer le cas \(\theta-\theta_n\geq 0\) du cas \(\theta-\theta_n < 0\). Nous n'étudierons que le premier cas, l'autre est laissé en exercice.
Si \(\theta-\theta_n\geq 0\), alors la rotation suivante se fait dans le sens trigonométrique donc \(s_{n+1}=1\). D'après \((\ref{eq:approxtheta})\), on a
\begin{equation}\label{ineq:Sn} 0\leq \theta-\underbrace{\sum_{i=0}^{n}s_i\gamma_i}_{\theta_n}\leq\gamma_n \end{equation}et donc en soustrayant l'angle \(\gamma_{n+1}\) :
\begin{align*} -\gamma_{n+1}&\leq \theta-\sum_{i=0}^{n}s_i\gamma_i-s_{n+1}\gamma_{n+1}\leq\gamma_n-\gamma_{n+1}\\ -\gamma_{n+1}&\leq \theta-\sum_{i=0}^{n+1}s_i\gamma_i\leq\gamma_n-\gamma_{n+1}. \end{align*}On en déduit que
\begin{equation} \Big|\theta-\theta_{n+1}\Big|\leq\max\{\gamma_{n+1},\gamma_{n}-\gamma_{n+1}\}. \end{equation}Ainsi pour que l'encadrement \((\ref{ineq:Sn})\) soit satisfait au rang \(n+1\), c'est-à-dire \(0\leq\theta-\theta_{n+1}\leq\gamma_{n+1}\), il faut que \(\gamma_n-\gamma_{n+1}\leq\gamma_{n+1}\), ce qui nous donne finalement la condition nécessaire et suffisante suivante :
\begin{equation}\label{ineq:exo} \forall n\in{\mathbf N},\quad \frac{1}{2}\gamma_n\leq\gamma_{n+1}\leq\gamma_n. \end{equation}Autrement dit, la décroissance des angles pour l'approximation ne peut pas être trop rapide, chaque nouveau pas doit être inférieur au pas précédent mais doit également rester supérieur ou égal à la moitié de ce pas. Pour achever la preuve par récurrence de \((\ref{ineq:Sn})\), il faut que la condition initiale \(|\theta-s_0\gamma_0|\leq\gamma_0\) soit satisfaite.
L'algorithme cordic
Nous connaissons à présent les contraintes sur l'angle \(\theta\) à approximer et sur la suite des angles prédéfinis \((\gamma_n)_{n\in{\mathbf N}}\) pour ce faire, mais quelle suite utiliser ?
La rotation \(R_i\) définie en \((\ref{eq:cosfact})\) engendre une multiplication de chaque coordonnée du point \(N\) par la valeur \(\tan\gamma_i\) avec éventuellement un changement de signe. Il nous faut à la fois contourner le calcul de la tangente (sans quoi nous serions revenus au point de départ, l'objectif initial étant précisément de calculer efficacement les fonctions trigonométriques) et optimiser ces deux produits. La multiplication (resp. la division) par \(2\) se traduit en machine par un simple décalage des bits de la représentation binaire des nombres à gauche (resp. à droite) et une telle opération est réalisée en un seul cycle machine**c'est également le cas pour un décalage circulaire d'un nombre arbitraire de positions avec un circuit spécialisé appelé barrel shifter. On va donc fixer \(\tan\gamma_i=2^{-i}\), soit \(\gamma_i=\arctan 2^{-i}\) :
\begin{equation}\label{eq:cosarctan} R_i=\cos(\arctan 2^{-i})\begin{pmatrix} 1& -s_i2^{-i}\\ s_i2^{-i} & 1 \end{pmatrix}. \end{equation}Avant de poursuivre la simplification des calculs, vérifions tout d'abord que la somme de la série de terme général \(\gamma_i:=\arctan 2^{-i}\) existe.
D'après l'exercice précédent, on a \(\arctan 2^{-i}<2^{-i}\) et donc \begin{equation*} \sum_{i=0}^{n}\arctan 2^{-i}<\sum_{i=0}^{n}2^{-i} =\frac{1}{2^n}\sum_{i=0}^{n}2^i=\frac{1}{2^n}2^{n+1}=2. \end{equation*}
La série étant croissante et majorée, elle converge. Expérimentalement, on obtient
\begin{equation}\label{eq:bound} {\color{#0AF}\theta_\infty}:=\lim_{n\rightarrow+\infty}\sum_{i=0}^n\arctan 2^{-i}\simeq1.743286620472341 \end{equation}il faut donc que \(\theta\in[-{\color{#0AF}\theta_\infty},{\color{#0AF}\theta_\infty}]\) ce qui est satisfait puisque nous avons supposé que \(\theta\in[0,\frac{\pi}{2}]\). Il faut encore s'assurer que la suite \((\gamma_n)_{n\in\mathbf N}\) satisfait \((\ref{ineq:exo})\). La fonction \(\arctan\) étant strictement croissante sur \([0,\frac{\pi}{2}]\), il ne reste qu'à vérifier que pour tout entier \(n\) on a \(\frac{1}{2}\gamma_n\leq\gamma_{n+1}\) autrement dit que \[\forall n\in{\mathbf N},\quad \arctan 2^{-n}\leq2\arctan 2^{-n-1}.\] La preuve est laissée en exercice.
Reprenons l'étude des rotations à partir de \((\ref{eq:cosarctan})\). La matrice de la composition de deux rotations étant égale au produit des matrices de ces rotations, la matrice \(\Theta_n\) obtenue après \(n+1\) rotations est donnée par l'équation
\begin{equation}\label{eq:avant} \Theta_{n}:=\prod_{i=0}^nR_i=\prod_{i=0}^n \cos(\arctan 2^{-i})\begin{pmatrix} 1& -s_i2^{-i}\\ s_i2^{-i} & 1 \end{pmatrix}. \end{equation}En notant \(K_n\) le produit
\begin{equation} K_n:=\prod_{i=0}^n\frac{1}{\sqrt{1+2^{−2i}}} \end{equation}la matrice devient
\begin{equation} \Theta_{n}=K_n\prod_{i=0}^n\begin{pmatrix} 1& -s_i2^{-i}\\ s_i2^{-i} & 1 \end{pmatrix}. \end{equation}Le point de départ étant le point \(I=(1,0)\), l'approximation des fonctions cosinus et sinus après \(n+1\) rotations sont fournies par
\begin{equation} \color{#EEF} \label{eq:finale} (\cos\theta,\sin\theta)\approx \prod_{i=0}^n\begin{pmatrix} 1& -s_i2^{-i}\\ s_i2^{-i} & 1 \end{pmatrix} \begin{pmatrix} K\\ 0 \end{pmatrix} \end{equation}Maintenant que la méthode a été validée, il ne reste plus qu'à écrire l'algorithme. Le sens de la rotation courante étant fixé par la position de \(N\) par rapport à \(M\), c'est-à-dire la différence entre l'angle \(\theta\) et son approximation, il faut précalculer les valeurs \(\gamma_i=\arctan 2^{-i}\) nécessaires selon la précision souhaitée, ici nous avons supposé \(n=27\).
Cordic(theta) données theta: réel constantes K ← 0.6072529350088814 ATAN ← table des arctan 2**(-i) n ← 27 variables i, s: entier x, y, aux, ecart: réels DEBUT x ← K y ← 0 ecart ← theta i ← 0 TQ (i ≤ n) FAIRE s ← signe(ecart) aux ← x - (s * (y >> k)) y ← y + (s * (x >> k)) x ← aux ecart ← ecart - (s * ATAN[i]) FTQ RENVOYER (x,y) FIN
L'erreur commise en remplaçant chaque coefficient \(K_n\) par la limite \(K\) est bien visible pour les premières occurrences, mais s'estompe rapidement.
Travaux pratiques