关于DSP除法运算,如果不需要特别高的精度,可以采用查找表的方式,优化除法运算。下面我们就研究一下如何采用查找表的方式加速除法运算。我们以32位单精度float类型除法为例来讲解。
FLOAT的内存结构格式
float一共32位,其结构定义如下:
|-------- 31 -------|------------ 30-23------------ |------------ 22-0 ------------|
符号位(sign) 指数部分(exp) 小数部分(mag)
sign:符号位就一位,0表示正数,1表示负数
exp: 指数部分,无符号正数
mag:小数部分,定点小数,小数点在最左边,表示范围:0 ≤ mag< 1。
float的表达式 : pow(-1, sign) * (1 + mag) * pow(2, exp - 127)
示例程序1
#include
#include
#include
intmain(
int argc,
char*argv[])
{
float f;
int i;
int sign;
int exp;
int mag;
float d_mag;
float f2;
f = 1.23;
//f = -0.12;
i = *(
int*)&f;
sign = (i >> 31) & 0x01;
exp = (i >> 23) & 0xFF;
mag = i & 0x7FFFFF;
d_mag = 1.0f * mag /0x800000;
f2 = (sign == 0 ? 1 : -1) * (1 + d_mag) * pow(2.0, exp - 127);
printf(
"float: f = %f
",f);
printf(
"sign = %X, exp = %X, mag =%X
",sign, exp, mag);
printf(
"float: f2 = %f
",f2);
return 0;
}
输出结果:
浮点数(FLOAT)的倒数公式
倒数(reciprocal / multiplicative inverse)读(dàoshù),是指数学上设一个数x与其相乘的积为1的数,记为1/x或x,过程为“乘法逆”,除了0以外的复数都存在倒数,倒数图将其以1除,便可得到倒数.
两个乘积是1的数互为倒数,0没有倒数。
倒数,一般在计算器中表示为1/x。
所以浮点数倒数
inv = 1 / f
= 1 / (pow(-1,sign) * (1 + mag) * pow(2, exp - 127)
)
= pow(-1, sign) * pow(2, 127 - exp) * (1 / * (1 + mag))
示例程序2
#include
#include
#include
intmain(
int argc,
char*argv[])
{
float f;
int i;
int sign;
int exp;
int mag;
float d_mag;
float inv;
f = 1.23;
//f = -0.12;
i = *(
int*)&f;
sign = (i >> 31) & 0x01;
exp = (i >> 23) & 0xFF;
mag = i & 0x7FFFFF;
d_mag = 1.0f * mag /0x800000;
inv = (sign == 0 ? 1 : -1) * pow(2.0, 127 - exp) * 1.0 / (1 + d_mag);
printf(
"float: f = %f, 1 / f = %f
", f, 1 / f);
printf(
"float: inv = %f
",inv);
return 0;
}
输出结果:
采用查找表计算倒数
倒数的快速运算的关键是计算(1 / * (1 + mag)),其中
最直接的想法莫过于将[0, 1)区间n等分,建立查找表。可以根据存储空间与计算精度的要求综合考虑n的大小。比如经过权衡,我们采用n = 4096,这时就需要16kbytes的存储空间。有效精度可以达到3~4位。
实例程序3:
#include
#include
#include
floatdiv_table[4096];
voidcreate_div_table_4096()
{
int i;
for(i = 0; i < 4096; i++)
{
div_table[i]= 1.0 / (1 + i / 4096.0);
}
}
intmain(
int argc,
char*argv[])
{
float f;
int i;
int sign;
int exp;
int mag;
float d_mag;
float inv;
create_div_table_4096();
f = 1.23;
//f = -0.12;
i = *(
int*)&f;
sign = (i >> 31) & 0x01;
exp = (i >> 23) & 0xFF;
mag = i & 0x7FFFFF;
inv = (sign == 0 ? 1 : -1) * pow(2.0, 127 - exp) * div_table[mag>> 11];
printf(
"float: f = %f, 1 / f = %f
", f, 1 / f);
printf(
"float: inv = %f
",inv);
return 0;
}
输出结果:
代码优化
待续…
参考资料
http://blog.csdn.net/adream307/article/details/7246993
http://baike.baidu.com/link?url=jtx4w2u7UBMh-jNP44zqOxsOPBrlq85o_ujklz-2Pcx92uYYgxKhzoxmqYPxvXmapeJCkpVYLjEcyDfDP_fjOygDNqTuWp6ADcjM2z6VG37