缘起
在MCU(Micro Control Unit)编程中,因为其处理能力有限,对浮点数的支持性能也一般,但有时我们希望在MCU上实现一些数学运算,比如固定长度FFT,比如对数运算,等等。这种情况下,如果直接使用math
数学函数库,会带来巨大的性能影响。为此,可以采用空间换时间的方式,通过使用查找表(Look-Up-Table
),在满足计算精度的要求下,实现快速的运算。本文就通过查找表的方式,探讨一种针对单精度浮点数的快速对数运算的方法。
单精度浮点数的表示
在计算机上,单精度浮点数是一个32位数字,包含1个符号位,8个指数位,和23个尾数位,如下图所示:
对于对数运算来说,数字必须为正,所以这里sign
必须是1。对上述浮点数进行对数(log10
)运算,我们得到:
log10(num) = (E - 127) x log10(2) + log10(1.b22b21...b0)
其中,等式右边的第一部分为普通的乘法运算(E = b30b29...b23
),第二部分是针对尾数部分的对数运算,是我们讨论的重点。
计算精度及查表法实现
在进一步讨论查表法实现的方法之前,先探讨计算机进行对数运算的精度问题。我们来看如下的函数公式:
y = lg(x) = ln(x) / ln(10)
其中lg
表示以10为底的对数运算,ln
表示以e
为底的对数运算(自然对数)。那么我们得到y
的微分如下:
dy = (1 / (x ln(10)) dx = dx / x / ln(10)
也就是说,数值计算的精度(dy
),与x
的大小成反比,x
越大,计算误差dy
越小。对于上一节提到的单精度浮点数计算公式来说,计算精度主要取决于第二部分对尾数部分的运算。由于尾数部分取值在[1, 2)
区间内,即1 <= x < 2
,计算误差最大的位置在当x == 1
时。为此,若只考虑最大计算误差,我们可以简化误差计算公式为:
dy = dx / 1 / ln(10) = dx / ln(10)
对于完整的尾数部分,其数值表达精度为1 / 2^23 = 0.00000011920929 = 1.1920929 * 10e-7
,因此计算误差(假设计算过程不引入新的误差)为1.7198265 * 10e-7
。
但是,在实际应用中,我们可能并不需要这么高的精度。例如,如果对数计算结果是用来表示信号的功率的分贝(dB
)值,那么0.01dB就是一个不错的精度了。假设我们的目标精度是0.01dB,我们看看具体是什么情况。根据dB计算的公式,假设信号功率为x
,那么转换成dB
的公式为:
z = 20 * log10(x) = 20 lg(x)
那么dz = 20 dy = dx * 20 / ln(10)
,假设dz = 0.01
,那么dx = dz * ln(10) / 20 = 0.00115
,也就是说,只要x
的精度好于0.00115
,我们就可以达到0.01dB的精度。我们来看看用几位尾数精度可以达到0.00115的精度:
尾数比特数 | 精度 | 分数表示精度 |
---|---|---|
1 | 0.5 | 1/2 |
2 | 0.25 | 1/4 |
3 | 0.125 | 1/8 |
4 | 0.0625 | 1/16 |
5 | 0.03125 | 1/32 |
6 | 0.015625 | 1/64 |
7 | 0.0078125 | 1/128 |
8 | 0.00390625 | 1/256 |
9 | 0.001953125 | 1/512 |
10 | 0.0009765625 | 1/1024 |
11 | 0.00048828125 | 1/2048 |
12 | 0.000244140625 | 1/4096 |
13 | 0.0001220703125 | 1/8192 |
从表中我们可以看到,当尾数比特数为10时,其精度达到了0.0009765625,好于0.01dB所要求的尾数精度0.00115。所以,我们可以取10比特尾数表示单精度浮点数,并构建一个1024(2^10
)个表项的查找表来实现该对数运算:
/** Log Table for [1, 2), with step 0.0009765625 */
const float Log10Table[1024] = {
0, 0.000423909, 0.000847404, 0.00127049, 0.00169316, 0.00211542, 0.00253727, 0.00295871,
0.00337974, 0.00380036, 0.00422058, 0.00464039, 0.0050598, 0.0054788, 0.0058974, 0.00631559,
0.00673338, 0.00715077, 0.00756776, 0.00798435, 0.00840054, 0.00881633, 0.00923173, 0.00964672,
0.0100613, 0.0104755, 0.0108893, 0.0113028, 0.0117158, 0.0121284, 0.0125407, 0.0129525,
0.013364, 0.013775, 0.0141857, 0.014596, 0.0150059, 0.0154154, 0.0158246, 0.0162333,
0.0166417, 0.0170497, 0.0174572, 0.0178645, 0.0182713, 0.0186777, 0.0190838, 0.0194895,
0.0198948, 0.0202998, 0.0207043, 0.0211085, 0.0215123, 0.0219157, 0.0223188, 0.0227215,
0.0231238, 0.0235257, 0.0239273, 0.0243285, 0.0247293, 0.0251298, 0.0255299, 0.0259296,
0.0263289, 0.0267279, 0.0271265, 0.0275248, 0.0279227, 0.0283202, 0.0287174, 0.0291142,
0.0295106, 0.0299067, 0.0303024, 0.0306977, 0.0310927, 0.0314874, 0.0318816, 0.0322756,
0.0326691, 0.0330623, 0.0334552, 0.0338477, 0.0342398, 0.0346316, 0.035023, 0.0354141,
0.0358048, 0.0361952, 0.0365852, 0.0369749, 0.0373642, 0.0377532, 0.0381418, 0.0385301,
0.0389181, 0.0393057, 0.0396929, 0.0400798, 0.0404664, 0.0408526, 0.0412384, 0.041624,
0.0420091, 0.042394, 0.0427785, 0.0431626, 0.0435465, 0.04393, 0.0443131, 0.0446959,
0.0450784, 0.0454605, 0.0458423, 0.0462238, 0.0466049, 0.0469857, 0.0473661, 0.0477463,
0.0481261, 0.0485055, 0.0488847, 0.0492635, 0.0496419, 0.0500201, 0.0503979, 0.0507754,
0.0511525, 0.0515294, 0.0519059, 0.052282, 0.0526579, 0.0530334, 0.0534086, 0.0537835,
0.054158, 0.0545323, 0.0549062, 0.0552798, 0.055653, 0.056026, 0.0563986, 0.0567709,
0.0571429, 0.0575146, 0.0578859, 0.0582569, 0.0586277, 0.0589981, 0.0593681, 0.0597379,
0.0601074, 0.0604765, 0.0608453, 0.0612138, 0.0615821, 0.0619499, 0.0623175, 0.0626848,
0.0630517, 0.0634184, 0.0637847, 0.0641508, 0.0645165, 0.0648819, 0.065247, 0.0656118,
0.0659763, 0.0663405, 0.0667044, 0.067068, 0.0674312, 0.0677942, 0.0681569, 0.0685192,
0.0688813, 0.0692431,