咱们运用的处理器一般状况下,要么直接支撑硬件的浮点运算,比方某些带有FPU的器材,要么就只支撑定点运算,此刻对浮点数的处理需求经过编译器来完结。在支撑硬件浮点处理的器材上,对浮点运算的编程最便利的办法便是直接运用浮点类型,比方单精度的float来完结。可是在许多状况下,限于本钱、物料等要素,可供咱们运用的只需一个定点处理器时,直接运用float类型进行浮点类型的运算会使得编译器产生很多的代码来完结一段看起来非常简略的浮点数学运算,形成的成果是程序的执行时间明显加长,且其占用的资源量也会成倍地添加,这就涉及到了如安在定点处理器上对浮点运算进行高效处理的问题。
既然是定点处理器,那么其对定点数,或者说字面意义上的“整数”进行处理的功率就会比它处理浮点类型的运算要高的多。所以在定点处理器上,咱们运用定点的整数来代表一个浮点数,并规则整数位数和小数位数,然后便利地对定点数和浮点数进行转化。以一个32位的定点数为例,假定转化因子为Q,即32位中小数的位数为Q,整数位数则为31-Q(有符号数的状况),则定点数与浮点数的换算联系为:
定点数=浮点数×2^Q
例如,浮点数-2.0转化到Q为30的定点数时,成果为:
定点数=-2×2^30=-2147483648
32位有符号数的表明规模是:-2147483648到2147483647。假如咱们把有符号定点数的最大值2147483647转化为Q为30对应的浮点数,则成果为:
浮点数2147483647/2^30=1.999999999
从上面的两个核算比如中也能够看出,在Q30格局的状况下,最大的浮点数只能表明到1.999999999,假如咱们想把浮点数2.0转化为Q30的定点数,则产生了溢出,即形成了1e-9的截断误差。在此咱们列出Q0到Q30对应的规模和分辨率如下表所示:
假如你嫌自己核算费事的话,能够凭借Matlab的指令来求取它们的转化,例如,在Matlab的指令窗口中输入:
q = quantizer(‘fixed’, ‘ceil’, ‘saturate’, [32 30]);
FixedNum=bin2dec(num2bin(q,1.999999999));
回车之后就能够看到1.999999999转成Q30之后的定点数了。
弄清楚了单个浮点数和定点数之间的转化联系,接下来就需求了解一下两个定点数所代表的浮点数进行运算时,是怎么转化的了。依据乘法的结合律、分配率,浮点数转化之后的定点数是能够直接运算的,例如:
1. 不同Q格局的转化
设有定点数Fixed1=Float1*2^Q1,假如把它用为Q2这个不同精度/表明规模的定点数来表明,则有Fixed2=Float1*2^Q2。所以不同的Q格局直接的转化为:
Fixed2=Fixed1*2^Q2/2^Q1=Fixed1*2^(Q2-Q1)
由于Fixed1、Fixed2都是定点数,所以在C编程的状况下,咱们能够运用高效的左移、右移操作来完结这个乘以2^(Q2-Q1)的操作。
2. 两个相同Q格局的定点数:
Fixed1=Float1*2^Q
Fixed2=Float2*2^Q
则加法操作为:
Float1+Float2=Fixed1/2^Q+Fixed/2^Q=(Fixed1+Fixed2)/2^Q
关于上述的加法操作,假如定点数的和Fixed1+Fixed2超过了32位整数的极值,则会产生溢出现象,形成成果的不正确,此刻咱们只能先丢失一倍的精度,把Float1、Float2的Q值变为Q-1.
乘法操作为:
Float1*Float2=Fixed1/2^Q*Fixed/2^Q= Fixed1*Fixed2/2^(2Q)
相同的道理,假如Fixed1*Fixed2之后的定点数超过了32位整数的极值,则咱们也需求提早对它们进行一下折算,改换一下它们的Q值。这就涉及到对成果的一个预估问题,也是定点编程不如浮点编程简略、高效的缺乏之一。
3. 两个不同Q格局的定点数:
Fixed1=Float1*2^Q1
Fixed2=Float2*2^Q2
运算的规则是结合了前面的两种状况,只不过多了额定的转化作业:要么把其间的一个Q1格局的定点数先转化为另一个Q2格局,要么把它们都转化为一个中心值Q3格局的定点数,然后再进行运算。
这些运算尽管并不杂乱,可是假如在数学运算比较多的状况下,一个个的进行手艺转化仍是比较费事的,还好在近些年的处理器特别是DSP芯片中,在其BootROM中都内置了强壮的数学表来协助咱们完结这些转化作业,咱们只需依照必定的格局进行书写,那么编译器就会主动调用相关的库函数来完结了。以TI的C28x系列DSP为例,咱们能够运用现成的IQMath库来完结这些繁琐的作业。它的运用办法示例为
1)在工程特点中引证IQmath.lib库文件
2)在运用IQMath库函数的主程序中引证相关的头文件:
#include
#define PI 3.14159
_iq input, sin_out;
void main(void )
{
/* 0.25 x PI radians represented in Q29 format*/
input=_IQ29(0.25*PI);
sin_out =_IQ29sin(input);
}
其间,咱们能够在头文件中指定一个大局的Q格局,在不需求特别指定Q值的时分,运用默许的值。
例如,在头文件中#define Q 28,则咱们在程序中调用IQMath库函数时,
sin_out =_IQsin(input);//运用大局界说的Q28格局
sin_out =_IQ29sin(input); //特别指定运用Q29格局
默许状况下,编译器运用的Q格局是24,假如寻求更高的精度,则能够运用更大的Q值,可是相应地表明的浮点数的规模也要小,此刻能够考虑运用标么值,使得大部分变量的值都处在-1到1的区间内。
此外,在C言语编程时,调用方法是_IQsin(input),在C++编程时,则直接运用IQsin(input)就能够了。
3)在CMD链接文件中指明IQMath数学表的方位:
例如,关于281x器材:
MEMORY
{
PAGE 0:
PRAMH0 (RW) : origin = 0x3f8000, length = 0x001000
PAGE 1:
IQTABLES (R) : origin = 0x3FF000, length = 0x000b50
DRAMH0 (RW) : origin = 0x3f9000, length = 0x001000
}
SECTIONS
{
IQmathTables : load = IQTABLES, type = NOLOAD, PAGE = 1
IQmathTablesRam : load = DRAMH0, PAGE = 1
IQmath : load = PRAMH0, PAGE = 0
}
关于2833x器材:
MEMORY
{
PAGE 0:
PRAML0 (RW) : origin = 0x008000, length = 0x001000
PAGE 1:
IQTABLES (R) : origin = 0x3FE000, length = 0x000b50
IQTABLES2 (R) : origin = 0x3FEB50, length = 0x00008c
DRAML1 (RW) : origin = 0x009000, length = 0x001000
}
SECTIONS
{
IQmathTables : load = IQTABLES, type = NOLOAD, PAGE = 1
IQmathTables2 > IQTABLES2, type = NOLOAD, PAGE = 1
{
IQmath.lib
}
IQmathTablesRam : load = DRAML1, PAGE = 1
IQmath : load = PRAML0, PAGE = 0
}
关于280x器材:
MEMORY
{
PAGE 0:
PRAML0 (RW) : origin = 0x008000, length = 0x001000
PAGE 1:
IQTABLES (R) : origin = 0x3FE000, length = 0x000b50
IQTABLES2 (R) : origin = 0x3FEB50, length = 0x00008c
IQTABLES3 (R) : origin = 0x3FEBDC, length = 0x0000AA
DRAML1 (RW) : origin = 0x009000, length = 0x001000
}
SECTIONS
{
IQmathTables : load = IQTABLES, type = NOLOAD, PAGE = 1
IQmathTables2 > IQTABLES2, type = NOLOAD, PAGE = 1
{
IQmath.lib
}
IQmathTables3 > IQTABLES3, type = NOLOAD, PAGE = 1
{
IQmath.lib
}
IQmath : load = PRAML0, PAGE = 0
}
为了便利数学运算的高效处理,IQMath库中还包含了常用的数学运算函数,包含:
1. 格局转化
IQN浮点转定点,IQNtoF定点转浮点,atoIQN字符串转定点,IQNtoa定点转字符串,IQNint回来定点数的整数部分,IQNfrac回来定点数的小数部分,IQtoIQN和IQNtoIQ为指定Q格局与大局Q格局的互转,IQtoQN和QNtoIQ为32位与16位互转,IQmpy2, 4, 8..64即左移,IQdiv2, 4, 8..64即右移
2. 管用运算
IQNmpy和IQNrmpy:乘法,IQNrsmpy为带饱满的乘法。IQNmpyI32和IQNmpyI32int为定点数与32位整数的乘法,IQNmpyI32frac可回来成果的小数位数。QNmpyIQX:不同Q格局的定点数相乘。
IQNdiv:除法运算。
3. 三角运算
包含IQNasin,IQNsin,IQNsinPU,IQNacos,IQNcos,IQNcosPU,IQNatan2,IQNatan2PU,IQNatan。
其间,PU的意义在该函数中π现已折算为1。例如:
sin(0.25*π)=sinPU(0.25)。
4. 代数运算
包含IQNexp,IQNlog,IQNsqrt,IQNisqrt,IQNmag,IQNabs,IQsat