我需要在具有有限资源(内存,时钟速度)的MCU上将输入值(线性曲线)与输出值("对数或指数式"曲线)进行数学转换.
但这个问题是更普遍的C编程问题.
比方说,我们有两个变量:uint8_t input和uint8_t output.输入值是0-255.输出值也应该是0-255,但改变与输入不是线性的.
例如(input ==> output):
0 ==> 0
1 ==> 1
10 ==> 1
100 ==> 10
200 ==> 75
250 ==> 225
255 ==> 255
Run Code Online (Sandbox Code Playgroud)
让我们说,我可以进行数学转换,类似于(通常)"2 ^(输入/ 32)-1",我可以实现接近我需要的东西.我试过了:
output = (pow(2, (input/32)))-1
但是,根据uint8_t类型变量的限制,我将获得2:1,2,4 ......最大128(减1)的输出,它们之间没有平滑过渡:
0 ==> 0
1 ==> 0
10 ==> 0
100 ==> 7
200 ==> 63
250 ==> 127
255 ==> 127
Run Code Online (Sandbox Code Playgroud)
pow()由于MCU资源有限,我也不愿意使用.我可以通过在内存中使用表来实现所需的结果,但是(再次)它将花费256字节的ROM,这是不可接受的.我也可以用较小的桌子做近似,但......
可能还有其他一些方法可以用数学方法实现这个结果,uint8-uint32在低资源MCU上保留算术内部?
编辑:"正确"的公式和数字不存在.输出数字可能有所不同 在"input = 0"==>"output = 0"处,"input = 255"==>"output = 255".要求:最小和最快的编译代码.预期结果应该接近此图(在uint8_t中):
让我们说,我可以进行数学转换,类似于(通常)"2 ^(输入/ 32)-1",我可以实现接近我需要的东西.
您可以2^(input/32)通过使用余数进行线性插值来获得近似值.假设input是一个整数,您可以计算(1 + (input%32)/32.0) * pow(2, input/32)使用简单8位计算的等价物:
uint8_t f(uint8_t input) {
uint8_t t = (input & 31) + 32;
uint8_t u = input >> 5;
return u < 5 ? t >> (5 - u) : t << (u - 5);
}
Run Code Online (Sandbox Code Playgroud)
更新(数学背景)
我们想要近似f(input) = pow(2.0, input/32.0)需要浮点运算的函数,只需要整数运算.明显的方法是将范围0-255分成大小为32的子范围,并使用0, 32, 64, 96, 128, 160, 192仅使用整数计算的值,然后对其间的值进行简单的线性插值.现在假设我们想要近似函数值,input使得32*u <= input < 32*(u+1)where u是整数.我们知道最终的价值观:f(32*u) = 2^u和f(32*(u+1)) = 2^(u+1).使用线性插值我们得到以下近似值:
f_approx(input) = [f(32*(u+1))*(input - 32*u) + f(32*u)*(32*(u+1) - input)]/32
Run Code Online (Sandbox Code Playgroud)
现在让我们简化它:
f_approx(input) = [2^(u+1)*(input - 32*u) + 2^u*(32 - (input - 32*u))]/32
f_approx(input) = 2^u*[2*(input - 32*u) + 32 - (input - 32*u)]/32
f_approx(input) = 2^u*[(input - 32*u) + 32]/2^5
f_approx(input) = [(input - 32*u) + 32]*2^(u-5)
Run Code Online (Sandbox Code Playgroud)
现在,如果我们注意到这32是一种力量,2我们可以看到(input - 32*u)它是相同的,input % 32也可以计算为input & 31.从而
f_approx(input) = t*2^(u-5)
Run Code Online (Sandbox Code Playgroud)
并且三元组如果在return刚刚处理u-5使用移位来计算2的幂的符号.