實驗:CORDIC計算三角函數(shù)【C】
簡介:
? ? ? ? CORDIC(Coordinate Rotation Digital Computer)算法即坐標旋轉數(shù)字計算方法,是J.D.Volder1于1959年首次提出,主要用于三角函數(shù)、雙曲線、指數(shù)、對數(shù)的計算。該算法通過基本的加和移位運算代替乘法運算,使得矢量的旋轉和定向的計算不再需要三角函數(shù)、乘法、開方、反三角、指數(shù)等函數(shù)。(百度百科)
? ? ? ? CORDIC算法使用更適合計算機的運算來計算旋轉,通過矢量旋轉就可以得到三角函數(shù)、反三角函數(shù)的值,通過雙曲旋轉能計算雙曲三角函數(shù)、開方、指數(shù)等,它的應用非常廣泛,這里就只提關于Cordic計算三角函數(shù)的部分。
簡單提一下原理:
首先推一下旋轉的迭代公式,這里用復數(shù)乘法推。

如圖,矢量(x0,y0)逆時針旋轉θ角度后得到(x1,y1),
那么有 ,

根據(jù)公式

?以及 復數(shù)乘法加法的運算律得到

所以有

提出cos(θ)或者sin(θ)就得到了矢量旋轉的迭代公式

這里需要乘tan(θ),試著把它變成移位運算,我們知道右移n位相當于乘以2^(-n),這里用excel計算一下對應的角度值θ。

表中的角度值θ有兩個良好的性質:
第一:將θ從0到15求和得到99.882°,這說明從0到15迭代的旋轉范圍在[-99.882°,99.882°]。
第二:

,這是一個很好的性質,表格中上下兩個角度的比值都接近0.5,如果輸入角在[-90°,90°]間,旋轉角度可以類似二分法那樣去靠近輸入角度。
現(xiàn)在可以用右移來代替乘tan(θ),還剩一個乘cos(θ)需要處理,

把cos(θ)去掉,與真正的旋轉相比少了一個拉伸,這也被稱為偽旋轉,如果迭代次數(shù)固定,那么拉伸系數(shù)也是固定的。

固定迭代16次,求出每次的拉伸系數(shù)再乘起來就得到了最終的拉伸系數(shù)K,在excel中算出K=0.607252935。
(順便提一下,提出sin的公式的k=4.56846E-37,這非常小,寫代碼的話不好搞。)
得到k的值后,就能計算向量旋轉的近似值了,比如求向量(1,0)逆時針旋轉θ度后的向量,可以用(1,0)偽旋轉迭代16次后乘以k得到近似值,優(yōu)化一下,k和(1,0)都是固定值,把k*(1,0)先算得到新的初值,那就變成:用(k,0)偽旋轉迭代16次。
得到旋轉后的向量再根據(jù)下面的公式就能求三角函數(shù)了。

測試代碼(部分,完整的在https://blog.csdn.net/qq_39892910/article/details/128895614?spm=1001.2014.3001.5502):
int?Ang[16]={4500000,2656505,1403624,712502,357633,178991,89517,44761,22381,11191,5595,2798,1399,699,350,175};
int cos_core(int a)
{
???? char i;
???? int x=30363,y=0,temp;//18次迭代,先連續(xù)2次45°旋轉作為90°旋轉,輸入角度范圍擴大至[-189.8812173,189.8812173]
???? if(a>=18988122||a<=-18988122)
???? ????return 0;???
???? if(a>=0)
???? {
???????? temp=x-y;
???????? y+=x;
???????? x=temp;
???????? temp=x-y;
???????? y+=x;
???????? x=temp;
???????? a-=9000000;
???? }
???? else if(a<0)
???? {
???????? temp=x+y;
???????? y-=x;
???????? x=temp;
???????? temp=x+y;
???????? y-=x;
???????? x=temp;
???????? a+=9000000;
???? }
???? for(i=0;i<16;i++)
???? {
???????? if(a>=0)
???????? {
???????????? temp=x-(y>>i);
???????????? y+=(x>>i);
???????????? x=temp;
???????????? a-=Ang[i]; ??
???????? }
???????? else if(a<0)
???????? {
???????????? temp=x+(y>>i);
???????????? y-=(x>>i);
???????????? x=temp;
???????????? a+=Ang[i]; ?
???????? }
???? }
???? return x;
}
int atan2_core(int y,int x)
{
???? char i;
???? int temp,a;
???? a=0;
???? if(y<0)
???? {
???????? temp=x-y;
???????? y+=x;
???????? x=temp;
???????? temp=x-y;
???????? y+=x;
???????? x=temp;
???????? a-=9000000;
???? }
???? else if(y>0)
???? {
???????? temp=x+y;
???????? y-=x;
???????? x=temp;
???????? temp=x+y;
???????? y-=x;
???????? x=temp;
???????? a+=9000000;
???? }
???? else
???? {
???????? if (x<0)
???????? return 18000000;
???????? else //兩個情況,但atan2(0.0,0.0)返回0.0,所以直接返回0
???????? return 0;
???? }
???? for(i=0;i<16;i++)
???? {
???????? if(y<0)
???????? {
???????????? temp=x-(y>>i);
???????????? y+=(x>>i);
???????????? x=temp;
???????????? a-=Ang[i]; ??
???????? }
???????? else if(y>0)
???????? {
???????????? temp=x+(y>>i);
???????????? y-=(x>>i);
???????????? x=temp;
???????????? a+=Ang[i]; ?
???????? }
???????? else
???????? ? return a;
???? }
???? return a;
}
輸出:
? ? ? ? 時間:我在單片機平臺上復制了一份一樣的代碼,PC的輸出結果與單片機的輸出結果截然不同。
PC輸出:

單片機(STM32F103)輸出:

經(jīng)過對比:單片機上CORDIC比math的三角函數(shù)快很多,PC上math的三角函數(shù)比CORDIC快很多。兩個平臺在許多方面有巨大差異,不清楚是什么原因導致了這種情況。
誤差:
(這里的誤差是以math的三角函數(shù)輸出值為參考,兩者相減的絕對值作為誤差)
在這方面,PC與單片機的輸出結果相同:

反正切atan2:


余弦:


觀察到cos(180°)、cos(0°)等的輸出超出了范圍,得限制一下輸出,其他看起來感覺還湊合,我會在單片機平臺上使用它。
如果按照上面代碼來運行,旋轉角度不會與輸入角度相等。在我這個思路中,初值固定,所以迭代次數(shù)也固定,就算旋轉角度早已與輸入重合,它也要走完剩下的。當然這是有解決辦法的,也有很多地方可以優(yōu)化,我這寫的比較粗糙,我自己湊合著用。