欢迎光临东莞市飞江电子科技有限公司官网!
全国咨询热线

13926563901

18925580829

飞江淘宝店铺

首页>技术资料>51单片机

使用位运算代替乘除法

发布时间:2018-03-26   浏览量:

已知 :

unsigned int temp=1000;
     unsigned int result=0;
要求出result = value * 10%
最直接的方法是     :  result = (temp * 10) / 100;
使用位运算的方法是 :  result = (temp >> 4) + (temp >> 5) + (temp >> 8) + (temp >> 9);
MPLAB(PICC-V9.70-Lite Mode)下测试得到的结果显示使用位运算需要的时间仅为乘除法运算的4分之1
先看测试代码、比较一下两种表示方法,再来看如何使用位运算的表示方法。
 
测试代码 :
#include
__CONFIG(0x3f3a);
unsigned int temp=1000;
unsigned int result1=1;
unsigned int result2=1;
int main(void)
{
    result1 = (temp>>4) + (temp>>5) + (temp>>8) + (temp>>9);  //位运算
    asm("nop");
    asm("nop");
    result2 = ( temp *10 )/100;                             //乘除法
    asm("nop");
    asm("nop");
    while(1) ;
}
MPLAB中没有更改晶振、的按默认的20MHz调试的。
使用’ asm("nop");’是为了调试的方便。
结果为 :result1 = 97result2 = 100;
由位运算求出的10%误差是3%、而乘除法的结果没有误差。
在很多情况下都不需要绝对精确的结果、所以上面的差值3是完全可以接受的。
下面来看看使用位运算和乘除法需要的执行时间 :
 
Instruction Cycles
Time(uSecs)
位运算
222
444
乘除法
851
1702
使用位运算需要的时间大概是乘除法运算的4分之1
在反汇编代码中可以看到、乘除法运算过程中调用了乘法和除法的函数(MPLAB自带的) :
乘法 :
0x709处定义的乘法 :
 
除法 :
 
0x734处定义的除法:
 
执行乘发和除法需要的时间为 :
 
Instruction Cycles
Time(uSecs)
__wmul
305
610
__lwdiv
505
1010
执行乘法或除法需要的时间都远远比整个位运算多得多。
 
使用这种位运算有几个限制条件 :
1、需要知道系数、比如上面的10%、当然、这个系数是120%、或是8倍都是一样的。
2、结果允许一定的误差、比如上面的97、差值是3。这个误差可大可小、接下来会讲到。
 
现在来看看上面使用的位运算 :
temp10%的表达式
result = (temp >> 4) + (temp >> 5) + (temp >> 8) + (temp >> 9);
是怎么来的呢。
x/64、用移位表示就是x>>6、知道这种表示法、自然就会明白了。
10%用二进制表示出来就是 :
1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9) = 0.099609375、即9.96%、约等于10%
所以结果就是 :
= temp * (  1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9)  )
= temp / (2^4) + temp / (2^5) + temp / (2^8) + temp / (2^9)
= (temp >> 4) + (temp >> 5) + (temp >> 8) + (temp >> 9)
这里注意要加括号’()’、因为移位运算的优先级比加减法低
10%用到减法来表示的情况 :
(1/(2^3) - 1/(2^5) + 1/(2^7) -1/(2^9)) = 0.099609375
计算时使用天下计算器之类的计算软件会很快的得到结果 :


误差 :
方法很简单、但是要注意误差的存在。
我们的表达式里面有4个部分1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9)
如果只要三项1/(2^4) + 1/(2^5) + 1/(2^8)、结果就是0.09765625=9.76%
误差增大了、要得到越小的误差、需要的项数就越多。
使用5项时误差更小1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9) + 1/(2^11)= 0.10009765625=10.00%
但是使用的项数运算量越大,而且移位的位数越大、运算量也越大。
temp>>9需要移位次、temp>>11需要移位10次。
所以只要误差可接受即可、不必耗费更多的代价来得到更高的精度。
 
空间 :
当然、最好的方法还是将结果做成数组、使用查表取值。
没有运算、不用考虑耗时、也不用担心运算出错。
比如将255分成100份 :
const unsigned char table[100]={
2, 5, 7, 10, 12, 15, 17, 20, 22, 25,    //1%~10%
……
.
……                                    //90%~100%
};
耗费flash中的100Byte并不算多、很多时候flash都用不完的。
 
 
'1/(2^1)=0.5
'1/(2^2)=0.25
'1/(2^3)=0.125
'1/(2^4)=0.0625
'1/(2^5)=0.03125
'1/(2^6)=0.015625
'1/(2^7)=0.0078125
'1/(2^8)=0.00390625
'1/(2^9)=0.001953125
'1/(2^10)=0.0009765625