摘要 在嵌入式系統中,由於沒有浮點運算單元,當涉及浮點運算的時候需要做定點化處理。查表法是一種常用的方法。表的大小直接關係到計算的精度和複雜度。如何在計算精度和複雜度之間取得平衡,是乙個重要的問題。本文根據泰勒公式重新設計了一種新的計算方法。這種方法具有很高的精度,而計算複雜度低,表的大小也很小。
在多**數字訊號處理中,經常要涉及一些非線性函式的計算。例如求開方,非整數冪次運算,對數運算,三角函式的計算等。嵌入式系統中的c編譯器雖然提供了相應的庫函式,但是在計算密集的地方要實時實現是比較困難的。查表法是一種常用的優化方法。採用這種方法必須根據自變數的範圍和精度事先製作一張查詢表。顯然,輸入的範圍越大,精度要求越高,則所需的**越大,需要的儲存空間越大。查表法所需的計算就是根據輸入值確定表的位址,根據表的位址就可以得到相應的函式值,因而運算量小。查表法比較適合非線性函式是週期函式或已知非線性函式的輸入的動態變化範圍的情況。本文以對數函式的計算為例闡述計算的過程。
查表法比較適合於週期函式或自變數的動態範圍比較小的場合。對於像對數函式log2這樣的非線性函式,輸入自變數為任意乙個大於0的實數,輸出為任意實數。輸入和輸出的變化範圍都很大,直接做表就很困難。有沒有一種好的方法來解決表的大小的問題呢?浮點數的規格化方法為我們提供了一種好的思路。
對於任意乙個實數x, 根據浮點數的規格化方法,可以把它寫成下面的表示式:
x = a * 2^m
其中, a 是介於[0.5,1.0]之間的乙個純小數,而m 是乙個整數。則求對數可以寫成:
log2(x) = log2(a * 2 ^m) = m + log2(a)
於是,求x的對數,實際上只要求解a的對數就可以了。而a的取值範圍為[0.5,1.0].
如何求得m呢?在嵌入式平台上,一般都有特殊的指令來求解這個值。例如,在tms320c2x/5x中有一條專門的指令norm用來對acc暫存器中的數進行規格書,而在arm上,armv5指令集中有一條clz指令來求前導零計數。
由於a的取值範圍在[0.5,1.0]之間,因此在[-1.0,0.0]之間。輸入和輸出均可以用q15的頂點格式表示。可以對a的範圍進行均勻劃分,每一段的對數值都取每個小區間的左端點的值,這樣就可以製作一張查詢表。
下表是把區間[0.5,1.0]均分為10個小區間生成的查詢表。
例如輸入為0.877,q15的定點格式為0.877*2^15 = 28738.
於是,查表所得的對數值為上述第二個表的第7個值,即 -7683.
提高查表精度的一種簡單的方法是採用線性插值的方法。既當計算所得的浮點index不是乙個整數的時候,採用線性逼近的方法。而本文採取的方法是用泰勒公式展開的方法,可有效提高計算精度。本文採用下面的泰勒公式展開:
泰勒展開式中的2階項分母裡面的2也可以放到導數裡面直接計算。x0是與查表位址相對應的輸入變數值。這樣,把它們做成**如下:
仍以上面的輸入為例:
0.877,q15的定點格式為0.877*2^15 = 28738
查表所得的index位址為第7個值,即 -7683.
泰勒公式表示式中: x – x0 = 28738 – 27853 = 885
經過一階導數計算值: ((x – x0) * 13904)>>13 = 1502;
經過二階導數的計算值:((((x-x0)*(x-x0))>>15)*(-8179))>>13 = -23;
綜上,直接查表得到的對數值為:-7683
加上二階導數的查表計算值為:-6181+ (-23)= -6204;
直接浮點計算的精確值log2(0.877) = -0.189351252217354
查一次表計算所得浮點值-7683/32768 = -0.234466552734375
查一階導數表計算所得浮點數值-6181/32768 = - 0.188629150390625
查二階導數表計算所得浮點數值-6204/32768 = -0.1893310546875
由此可見,經過二階導數查表計算所得的結果精度已經很高了,而計算只需3個乘法。
再舉一例:求解log2(10000)
分析與解答:輸入是乙個很大的正整數。首先求解出指數部分。
於是,只需要求解0.610351525的對數即可。0.610351525的q15格式為
0.610351525*2^15 = 20000
計算index位址為 ((20000-0.5*2^15)* 20 )>>15 = 2;
直接查表得到的對數值為:-24149
加上二階導數的查表計算值為:-23334+ (-7)= -23341;
而log2(0.610351525)的直接浮點運算值為-0.712287709,q15的定點表示為
-0.712287709*2^15 = 23340.24365,與上述二階導數查表計算的定點格式相等。
本文所述的方法具有運算精度高,查表**小的特點,計算複雜度低的特點。非常適合於在嵌入式裝置上實現。
本文所闡述的方法,不僅僅適用於以2為底數的對數的計算,也適合任意底對數的計算。同時,也非常適合計算其它一大類非線性函式。例如,開方運算,非整數冪次運算等。不管哪種運算,都只需要4個**,每個**10個16位數的資料量,計算只需要3個乘法和3個加法和**的載入操作。
注:本文系作者多年之前的一篇**改編
。
計算演算法時間複雜度的主方法的一種較為簡潔的記憶方法
這陣子在認真地看著演算法導論,之前看到第四章計算分治法的時間複雜度的計算方法被稱為 主方法 運用這個主方法可以快速地口算出分治演算法的遞迴式的時間複雜度,以下給出演算法導論裡關於主方法的描述吧,我就直接截圖 不得不說,演算法導論是一本非常偏向於數學的演算法書,裡面的對於各種演算法結論的正確性大都有著...
對付非線性可分的資料集的一種通用辦法
假設這個資料集能夠被乙個非線性的面分開,這個面的表達形式為f x,y,z 那麼,根據泰勒展開,可以得到f x,y,z 的多項式表達。那麼,就可以把其多項式表達作為乙個新的面,將多項式的各個次數的分量組成乙個新的向量,就成了線性可分的。這樣相當於將資料集做了個變換,將非線性的資料集變換成線性的資料集。...
聊一聊那些線性時間複雜度的排序演算法
實際上,基於比較和交換的排序演算法,它們的時間複雜度的下限就是o nlog2 n 氣泡排序,插入排序等自不必多說,時間複雜度是o n2 即使強如快速排序,堆排序等也只是達到了o nlog2 n 的複雜度。那麼那些傳說中可以突破o nlog2 n 下限,達到線性時間複雜度o n 的排序演算法到底是什麼...