已知 :
unsigned int temp=1000;
unsigned int result=0;
要求出result = value * 10%
最直接的方法是 : result = (temp * 10) / 100;
使用位運(yùn)算的方法是 : result = (temp >> 4) + (temp >> 5) + (temp >> 8) + (temp >> 9);
在MPLAB(PICC-V9.70-Lite Mode)下測(cè)試得到的結(jié)果顯示使用位運(yùn)算需要的時(shí)間僅為乘除法運(yùn)算的4分之1。
先看測(cè)試代碼、比較一下兩種表示方法,再來(lái)看如何使用位運(yùn)算的表示方法。
測(cè)試代碼 :
#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); //位運(yùn)算
asm("nop");
asm("nop");
result2 = ( temp *10 )/100; //乘除法
asm("nop");
asm("nop");
while(1) ;
}
在MPLAB中沒(méi)有更改晶振、的按默認(rèn)的20MHz調(diào)試的。
使用’ asm("nop");’是為了調(diào)試的方便。
結(jié)果為 :result1 = 97、result2 = 100;
由位運(yùn)算求出的10%誤差是3%、而乘除法的結(jié)果沒(méi)有誤差。
在很多情況下都不需要絕對(duì)精確的結(jié)果、所以上面的差值3是完全可以接受的。
下面來(lái)看看使用位運(yùn)算和乘除法需要的執(zhí)行時(shí)間 :
|
Instruction Cycles
|
Time(uSecs)
|
位運(yùn)算
|
222
|
444
|
乘除法
|
851
|
1702
|
使用位運(yùn)算需要的時(shí)間大概是乘除法運(yùn)算的4分之1。
在反匯編代碼中可以看到、乘除法運(yùn)算過(guò)程中調(diào)用了乘法和除法的函數(shù)(是MPLAB自帶的) :
乘法 :
即0x709處定義的乘法 :
除法 :
即0x734處定義的除法:
執(zhí)行乘發(fā)和除法需要的時(shí)間為 :
|
Instruction Cycles
|
Time(uSecs)
|
__wmul
|
305
|
610
|
__lwdiv
|
505
|
1010
|
執(zhí)行乘法或除法需要的時(shí)間都遠(yuǎn)遠(yuǎn)比整個(gè)位運(yùn)算多得多。
使用這種位運(yùn)算有幾個(gè)限制條件 :
1、需要知道系數(shù)、比如上面的10%、當(dāng)然、這個(gè)系數(shù)是120%、或是8倍都是一樣的。
2、結(jié)果允許一定的誤差、比如上面的97、差值是3。這個(gè)誤差可大可小、接下來(lái)會(huì)講到。
現(xiàn)在來(lái)看看上面使用的位運(yùn)算 :
求temp的10%的表達(dá)式
result = (temp >> 4) + (temp >> 5) + (temp >> 8) + (temp >> 9);
是怎么來(lái)的呢。
x/64、用移位表示就是x>>6、知道這種表示法、自然就會(huì)明白了。
10%用二進(jìn)制表示出來(lái)就是 :
1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9) = 0.099609375、即9.96%、約等于10%。
所以結(jié)果就是 :
= 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)
這里注意要加括號(hào)’()’、因?yàn)橐莆贿\(yùn)算的優(yōu)先級(jí)比加減法低
10%用到減法來(lái)表示的情況 :
(1/(2^3) - 1/(2^5) + 1/(2^7) -1/(2^9)) = 0.099609375
計(jì)算時(shí)使用天下計(jì)算器之類的計(jì)算軟件會(huì)很快的得到結(jié)果 :
誤差 :
方法很簡(jiǎn)單、但是要注意誤差的存在。
我們的表達(dá)式里面有4個(gè)部分1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9)、
如果只要三項(xiàng)1/(2^4) + 1/(2^5) + 1/(2^8)、結(jié)果就是0.09765625=9.76%
誤差增大了、要得到越小的誤差、需要的項(xiàng)數(shù)就越多。
使用5項(xiàng)時(shí)誤差更小1/(2^4) + 1/(2^5) + 1/(2^8) + 1/(2^9) + 1/(2^11)= 0.10009765625=10.00%。
但是使用的項(xiàng)數(shù)運(yùn)算量越大,而且移位的位數(shù)越大、運(yùn)算量也越大。
temp>>9需要移位次、temp>>11需要移位10次。
所以只要誤差可接受即可、不必耗費(fèi)更多的代價(jià)來(lái)得到更高的精度。
空間 :
當(dāng)然、最好的方法還是將結(jié)果做成數(shù)組、使用查表取值。
沒(méi)有運(yùn)算、不用考慮耗時(shí)、也不用擔(dān)心運(yùn)算出錯(cuò)。
比如將255分成100份 :
const unsigned char table[100]={
2, 5, 7, 10, 12, 15, 17, 20, 22, 25, //1%~10%
……
.
…… //90%~100%
};
耗費(fèi)flash中的100個(gè)Byte并不算多、很多時(shí)候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