2012-03-21 209 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; 
}