2.移位运算的设计实现 在很多VB的资料和代码中都用乘以2的方法实现左移,除以2的方法实现右移。这是可行的,也是有理论依据的。下图是一个BYTE类型的权值表: 位序号 7 6 5 4 3 2 1 0 权值 27 26 25 24 23 22 21 20 可以看出每一位的权值都是比它低一位的那一位的权值的2倍,对一个BYTE变量左移一位相当于每一个二进制位都向高位移动,则每一位的权值变为原来的两倍(最高位除外),由于BYTE变量的十进制值等于它的每个二进制位的值和该位权值的乘积的总和,所以把一个BYTE变量左移和把它的十进制值乘以2是等效的,唯一的区别就是如果BYTE变量的最高位为 1,乘以2会溢出,我们要使用一个小技巧防止溢出:先把最高位屏蔽为0,再乘以2就不会溢出了。据此我们可以写出把BYTE类型变量左移1位的函数: Private Function ShLB_By1Bit(ByVal Byt As Byte) As Byte‘把BYTE类型变量左移1位的函数,参数Byt是待移位的字节,函数返回移位结果‘(Byt And &H7F)的作用是屏蔽最高位。 *2:左移一位ShLB_By1Bit = (Byt And &H7F) * 2End Function 类似的把BYTE类型变量右移1位时采用除以2的方法 ,这时要注意舍去小数位,以免VB按照四舍五入的方法处理小数位而引起结果不正确。据此我们可以写出把BYTE类型变量右移1位的函数: Private Function ShRB_By1Bit(ByVal Byt As Byte) As Byte‘把BYTE类型变量右移1位的函数,参数Byt是待移位的字节,函数返回移位结果‘/2:右移一位ShRB_By1Bit = Fix(Byt / 2)End Function 有了移一位的函数,那么移任意位数的函数就不难写出了:只要反复的调用ShLB_By1Bit()或ShRB_By1Bit()就可以了,参见代码中的函数ShLB() 和 ShRB()。 至此字节变量的移位问题已经得到解决,现在再来看单字和双字的移位,它们分别对应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类型变量的移位了吗。这种方法完全绕过了有符号数的符号位给我们带来的众多麻烦,顺利的实现了目的。用这种方法需要注意一点:如果是左移,要保证把低字节的最高位移入高字节的最低位,反之如果是右移,要把高字节的最低位移入低字节的最高位。从下面的代码中可以看到实现的过程: Private Function ShLW_By1Bit(ByVal Word As Integer) As Integer'把一个字左移一位的函数, 参数Word是待移位的字,函数返回移位结果'INPUT-------------------------------'Word 源操作数'OUTPUT------------------------------'返回值 移位结果'last updated by Liu Qi 2004-3-24Dim 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 至于Long类型,和Integer类型一样,属于有符号数,也不能用乘以2和除以2的方法实现移位。我们只好和处理Integer类型一样如法炮制,用分而治之的方法实现移位。具体过程不再赘述,请参看代码。3.移位运算的性能优化 本文中的移位实现方法偏重于代码的可读性,没有优化代码的性能,因此不适用于对性能要求苛刻的场合。为了优化性能,可以用查表法来优化执行速度,这是一种拿空间换时间的方案,移位结果可以事先都计算出来,保存在移位表中,用的时候查表,比用*2,/2快多了。比如,字节类型的移位表数组定义如下: dim aSHLB(0 to 255,1 to 7) as byte'字节左移表 dim aSHRB(0 to 255,1 to 7) as byte'字节右移表 使用方法也很简单,比如想要求字节变量x左移一位的结果,只需使用aSHLB(x,1)就可得到,和函数调用很相似。当然,与函数调用不同的是,使用移位表之前一定要初始化移位表的所有元素,否则会得到错误的结果。 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下调试通过。

评论