目录
0. 目标
1. 对数核心分解
2. 为什么只需要近似 f ∈ [1,2)?
3. 二阶多项式近似公式
4. Q8 定点化(系数 369、185 的由来)
5. 归一化 f(代码最关键一步)
6. d 的 Q8 表示
7. 二阶多项式计算
8. 最终结果合并
9. 原理 → 代码 逐行对应表
10. 精度说明
0. 目标
计算:y=log2(x)×256
- 输出为Q8 定点数(小数放大 256 倍)
- 输入范围:x ∈ [1, 32768]
- 全程无浮点、无除法、全无符号、无溢出
1. 对数核心分解
任意正数 x 都可以写成:满足:
- k:整数,0≤k≤15
- f:小数部分,f∈[1,2)
两边取 log₂:
化简得到最核心公式:
- k 是整数部分,简单易求
- log2(f) 是小数部分,需要用多项式近似
2. 为什么只需要近似 f ∈ [1,2)?
因为:
- log₂(f) 是一条光滑、变化缓慢的曲线
- 无法直接在单片机上计算
- 用二阶多项式拟合可以做到误差极小、速度极快
令:d=f−1则:d∈[0,1)
我们要近似的目标函数变为:log2(1+d)
3. 二阶多项式近似公式(
log₂(1+d) 在 d∈[0,1) 的二阶泰勒展开为:
换成以 2 为底:
取前两项(二阶近似):
这就是代码中多项式部分的数学来源。
4. Q8 定点化(系数 369、185 的由来)
我们所有运算都基于Q8 定点(即数值 ×256):
- 1.4427×256≈369
- 0.7213×256≈185
所以定点化后的多项式为:
在代码中:
>>8等价于/256- 全部使用无符号整数运算
5. 归一化 f(代码最关键一步)
我们需要把 x 映射到 f ∈ [1,2),并转为 Q8 格式:
为了避免负数移位、溢出、浮点运算,统一写成:fQ8=(x≪8)≫k
对应代码:
uint32_t f_q8 = ((uint32_t)x << 8) >> k;这行保证:
- x < 256 时不会出错
- 全程无符号、无溢出
- 1 ~ 32768 全范围正确
6. d 的 Q8 表示
d=f−1转为定点:dQ8=fQ8−256
因为 f ∈ [1,2),所以:dQ8∈[0,255]完美适配无符号 uint32。
7. 二阶多项式计算
uint32_t d = f_q8 - 256; // d ∈ [0,255] uint32_t d2 = (d * d) >> 8; // d² 右移8位回到 Q8多项式:
uint32_t term1 = (d * 369) >> 8; uint32_t term2 = (d2 * 185) >> 8; uint32_t logf_q8 = term1 - term2;对应数学:
8. 最终结果合并
代码:
uint32_t res = ((uint32_t)k << 8) + logf_q8;9. 原理 → 代码 逐行对应表
10. 精度说明
- 二阶多项式最大误差:< 0.05 LSB(Q8)
- 远好于分段线性插值
- 运算量极小,适合 ISP、MCU、DSP 实时计算
- 全程无符号,彻底避免溢出与负数问题