2.移位运算的设计实现
在很多VB的资料和代码中都用乘以2的方法实现左移,除以2的方法实现右移。这是可行的,也是有理论依据的。下图是一个BYTE类型的权值表:
位序号 76543210
权值
2 7
2 6
2 5
2 4
2 3
2 2
2 1
2 0
可以看出每一位的权值都是比它低一位的那一位的权值的2倍,对一个BYTE变量左移一位相当于每一个二进制位都向高位移动,则每一位的权值变为原来的两倍(最高位除外),由于BYTE变量的十进制值等于它的每个二进制位的值和该位权值的乘积的总和,所以把一个BYTE变量左移和把它的十进制值乘以2是等效的,唯一的区别就是如果BYTE变量的最高位为 1,乘以2会溢出,我们要使用一个小技巧防止溢出:先把最高位屏蔽为0,再乘以2就不会溢出了。据此我们可以写出把BYTE类型变量左移1位的函数:
‘把BYTE类型变量左移1位的函数,参数Byt是待移位的字节,函数返回移位结果
‘(Byt And &H7F)的作用是屏蔽最高位。 *2:左移一位
ShLB_By1Bit = (Byt And &H7F) * 2
End Function
‘把BYTE类型变量右移1位的函数,参数Byt是待移位的字节,函数返回移位结果
‘/2:右移一位
ShRB_By1Bit = Fix(Byt / 2)
End Function
至此字节变量的移位问题已经得到解决,现在再来看单字和双字的移位,它们分别对应VB中的Integer和Long类型。用乘以2和除以2的方法还行吗?用几个数试验一下就会发现,这个方法失灵了。请看各种运算结果的对比:
A=1001’0111’1110’1100
右移一位: 0100’1011’1111’0110
(A/2):1100’1011’1111’0110
问题好象变的有点复杂了,其实导致这个方法失灵的最根本的原因是VB把Integer和Long类型当做有符号数理解,把一个有符号数乘以2或除以2,最高位(即符号位)根本就没有参与运算,这一点从上面的运算结果对比就可以看出来:把A除以2 以后最高位还是1,根本就没有变,而右移一位后最高位补入的是0,两种运算的结果自然是相去甚远。不只是符号位的问题,如果选用其它的数据来对比还会发现更多的问题,这里就不再赘述了。难道就真的没有办法了吗?办法当然是有的,既然已经实现了字节的移位操作,那么可以 用“分而治之”的策略,把Integer变量一分为二,拆成两个字节,把这两个字节交给ShLB()或ShRB(),把它俩各移一位,最后把移位后的两字节重新组合成一个Integer变量就是移位后的结果了,这不就实现了Integer类型变量的移位了吗。这种方法完全绕过了有符号数的符号位给我们带来的众多麻烦,顺利的实现了目的。用这种方法需要注意一点:如果是左移,要保证把低字节的最高位移入高字节的最低位,反之如果是右移,要把高字节的最低位移入低字节的最高位。从下面的代码中可以看到实现的过程:
'把一个字左移一位的函数, 参数Word是待移位的字,函数返回移位结果
'INPUT-------------------------------
'Word 源操作数
'OUTPUT------------------------------
'返回值 移位结果
'last updated by Liu Qi 2004-3-24
Dim HiByte As Byte, LoByte As Byte
'把字拆分为字节
HiByte = Hi(Word): LoByte = Lo(Word)
'把高字节左移一位,保证把低字节的最高位移入高字节的最低位
HiByte = ShLB_By1Bit(HiByte) Or IIf((LoByte And &H80) = &H80, &H1, &H0)
LoByte = ShLB_By1Bit(LoByte) '低字节左移一位
'把移位后的字节再重新组合成字
ShLW_By1Bit = Con(HiByte, LoByte)
End Function
3.移位运算的性能优化
本文中的移位实现方法偏重于代码的可读性,没有优化代码的性能,因此不适用于对性能要求苛刻的场合。为了优化性能,可以用查表法来优化执行速度,这是一种拿空间换时间的方案,移位结果可以事先都计算出来,保存在移位表中,用的时候查表,比用*2,/2快多了。比如,字节类型的移位表数组定义如下:
dim aSHRB(0 to 255,1 to 7) as byte'字节右移表
Integer类型的移位也可以用查表法,移位表占用 65535 * 15 * 2 * 2 个字节的内存空间。
移位表数组定义如下:
aSHLW(0 to &Hffff&,1 to 15) as integer'单字的左移表
aSHRW(0 to &Hffff&,1 to 15) as integer'单字的右移表
注意 :Integer是有符号类型,造表的时候要用它的无符号值来造表,同样查表的时候也要用它的无符号值来查表。(因为数组下标是不允许负数的。)
求Integer类型无符号值的方法是:(Int and &hFFFF&),注意,不等同于CLng(Int)
遗憾的是,Long类型无法造表,因为Long类型的值的范围是 4 GB,如果对它造表,那么表的大小就会超出总的内娴刂房占洹?
查表移位的代码请参见本文附带的代码,这里就不给出了。
三 结语
要想实现本文所述的那些位操作函数其实有很多方法,本文所用的方式未必是最好的,主要是为了提供一种解决问题的思路:在编程过程中遇到难以解决的问题时,想一想能不能把大问题分解成能解决或已解决的小问题,这就是“分而治之”的策略。因笔者水平有限,本文难免会有疏漏和不足之处,欢迎批评指正,有意见或建议给我发电子邮件liuqi5521@sina.com。
本程序在 Win2000+VB6.0下调试通过。
[1] [2]
