2012-03-21 223 views
4

基本上,我正在尋找C庫<math.h>中提供的log()exp()函數的實現。我正在使用8位微控制器(OKI 411和431)。我需要計算Mean Kinetic Temperature。要求是我們應該能夠儘可能快地計算MKT並儘可能少地使用代碼存儲器。編譯器自帶log()exp()函數<math.h>。但是調用函數和鏈接庫會導致代碼大小增加5千字節,這不適合我們使用的一個微型(OKI 411),因爲我們的代碼已經消耗了大約12K的可用〜15K代碼內存。高效實現自然對數​​(ln)和指數運算

我正在尋找的實現不應該使用任何其他C庫函數(如pow(),sqrt()等)。這是因爲所有庫函數都打包在一個庫中,即使調用了一個函數,鏈接器也會將整個5K庫帶到代碼存儲器中。

編輯

該算法應該是正確的3個小數位。

+3

當你有這樣的限制,你應該問自己什麼是你可以接受的精度?那麼可接受的誤差幅度是多少? – 2012-03-21 05:29:09

+0

@YochaiTimmer:忘了補充。感謝提醒。我編輯了我的問題。 :) – Donotalo 2012-03-21 05:37:27

+0

另外,什麼是輸入和輸出數字格式?定點如8.8?這聽起來像你將通過存儲相對於273開爾文的偏移量,即攝氏度而受益。 – Potatoswatter 2012-03-21 05:38:00

回答

6

泰勒級數爲電子^ x的收斂速度非常快,你可以調整你的實現你所需要的精度。 (http://en.wikipedia.org/wiki/Taylor_series

泰勒系列日誌不是很好......你有一個特定的部分,你需要實現的功能?

+0

如果我的計算正確,'ln'的輸入將在範圍[0.94,0.98]內。我猜taylor系列對於'ln'的近似也足夠了。 – Donotalo 2012-03-21 06:49:39

4

基值表與值之間的插值逼近工作嗎?如果數值範圍有限(這對您的情況很可能 - 我懷疑溫度讀數的範圍很大),並且不需要高精度,它可以工作。應該很容易在普通機器上測試。

下面是對功能的表格表示許多主題之一:Calculating vs. lookup tables for sine value performance?

+0

溫度範圍爲-22°F至158°F(-30°C至70°C),增量爲0.1°F。有1800個可能的溫度點,我想查找表是不夠的。 – Donotalo 2012-03-21 06:51:49

+0

@Donotalo,它仍然可能是一個選項(也可能不適用於您需要的精度) - 兩個exp/ln函數都是連續的,所以您可能需要的點數少得多,以獲得所需的結果精度。我沒有看到公式中直接使用溫度作爲exp/ln的參數,所以參數的實際範圍不同 - 很難預測稀疏表是否可行。 – 2012-03-21 16:12:09

3

如果你不需要浮點數學,你可以很容易地計算一個近似的小數基-2。首先將您的價值左移至32768或更高,並存儲您在count中完成的次數。然後,重複若干次(取決於你所希望的比例因子):

n = (mult(n,n) + 32768u) >> 16; // If a function is available for 16x16->32 multiply 
count<<=1; 
if (n < 32768) n*=2; else count+=1; 

如果上述循環被重複8次,然後數的對數底2將計數/ 256。如果十次,則計數/ 1024。如果十一,計數/ 2048。實際上,該函數通過計算n **(2^reps)的整數冪對數來工作,但中間值被縮放以避免溢出。

+0

上述算法中存在一個小錯誤: 移位該值後,count必須替換爲15 - count。 除此之外,有沒有更詳細的算法解釋? – user3528637 2017-11-30 11:59:25

2

使用泰勒級數不是最簡單的方法,也不是最簡單的方法。大多數專業實現都使用近似多項式。我將向您展示如何使用Remez algorithmMaple(這是一個計算機代數程序)中生成一個。

對於精度的3位數字在Maple執行以下命令:

with(numapprox): 
Digits := 8 
minimax(ln(x), x = 1 .. 2, 4, 1, 'maxerror') 
maxerror 

其響應是以下多項式:

-1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x 

隨着最大誤差:0。000061011436

Remez approximation of a function

我們生成近似於所述LN(x)的多項式,但是隻有[1..2]區間內。增加間隔並不明智,因爲這會增加最大誤差。取而代之的是,請執行下列操作分解:

formula

所以先找2的最大功率,這仍然比數量更小(參見:What is the fastest/most efficient way to find the highest set bit (msb) in an integer in C?)。這個數字實際上是以2爲底的對數。除以該值,則結果進入1..2區間。最後,我們將不得不添加n * ln(2)來獲得最終結果。

的示例實現對於數字> = 1:

float ln(float y) { 
    int log2; 
    float divisor, x, result; 

    log2 = msb((int)y); // See: https://stackoverflow.com/a/4970859/6630230 
    divisor = (float)(1 << log2); 
    x = y/divisor; // normalized value between [1.0, 2.0] 

    result = -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x; 
    result += ((float)log2) * 0.69314718; // ln(2) = 0.69314718 

    return result; 
} 

雖然如果打算只在[1.0,2.0]區間使用它,則該函數是這樣的:

float ln(float x) { 
    return -1.7417939 + (2.8212026 + (-1.4699568 + (0.44717955 - 0.056570851 * x) * x) * x) * x; 
}