• 高级位操作技巧


    【这里我们将要讨论一些高级的位操作技巧,如果能适当使用会大大提高你程序的运行速度】

    我们已经讨论了一些简单的位操作方法,如果你还不熟悉位操作可以先浏览一下【你必须知道的简单的位操作技巧】,这里我们接着往下说:

    技巧6:将最右边的数值为1的bit位设为0

    y = x & (x-1)

    终于开始有点意思了,技巧1到技巧5说实话是有点小儿科。

    这条语句将从右至左看值为1的比特位置为0。例如:整数00101010经过上述操作变成00101000,整数00010000经过操作变成0,因为只有一个比特位值为1。

    更多的例子:

    `   01010111    (x)
    &   01010110    (x-1)
        --------
        01010110
     
        01011000    (x)
    &   01010111    (x-1)
        --------
        01010000
     
        10000000    (x = -128)
    &   01111111    (x-1 = 127 (with overflow))
        --------
        00000000
     
        11111111    (x = all bits 1)
    &   11111110    (x-1)
        --------
        11111110
     
        00000000    (x = no rightmost 1-bits)
    &   11111111    (x-1)
        --------
        00000000

    观察这些例子你会发现有两种情况:

    1、这个数值存在值为1的比特位,减一就会将值为一的比特位右边的低位bit全置为1,自身变成0,再与原来的数与运算,得到的结果就是这一位置为0。

    2、这个数值是0,那么没有值为1的比特位,减一造成下溢出,所有比特位全变成1(有符号的整数),全0和全1做与运算结果为0。

    技巧7:隔离最右边值为1的比特位

    y = x & (-x)

    也就是找到一个数的最右边值为1的比特位,将其他位置为0。上面的语句实现的就是这个功能。例如01010100(黑体是最右边值为1的比特位)运算后结果为00000100。

    更多的例子:

    `   10111100  (x)
    &   01000100  (-x)
        --------
        00000100
     
        01110000  (x)
    &   10010000  (-x)
        --------
        00010000
     
        00000001  (x)
    &   11111111  (-x)
        --------
        00000001
     
        10000000  (x = -128)
    &   10000000  (-x = -128)
        --------
        10000000
     
        11111111  (x = all bits one)
    &   00000001  (-x)
        --------
        00000001
     
        00000000  (x = all bits 0, no rightmost 1-bit)
    &   00000000  (-x)
        --------
        00000000

    这项操作在补码表示的范围内有效,在二进制补码系统中-x表示为-x+1。

    我们再分成两种情况来看:

    1、存在最右边的值为1的比特位,我们以这位比特位中心(暂时标记为bi),把其它比特分为左右两边,右边的所有比特位都为0(bi-1,…,b0),左边的比特位不知道是啥(bn,…,bi+1)。

    好了,现在求-x,首先,将bi变位0,然后将bi-1,…,b0变位1,接着翻转bn,…,bi+1,最后再将所得结果+1。就得到了补码形式的-x。

    在+1之前由于bi-1,…,b0位都为1,加一后都为0,直到遇到bi位。

    总的来看,计算-x即是翻转bn,…,bi+1位,bi位不变,bi-1,…,b0都变成0。

    现在来看x & (-x)就很清楚了,即是将bn,…,bi+1置为0,bi位不变,bi-1,…,b0置为0。

    2、不存在值为1的比特位,值为0,0的二进制补码还是0,与运算结果还是0.

    我们严格阐述了这个技巧是正确的。

    技巧8:右传播最右边值为1的比特位

    y = x | (x-1)

    看一下例子就知道是怎么回事了,数01010000经过运算的到,寻找最右边值为1的比特位,然后将这一位右边的所有比特位置为1。

    这个技巧有个缺陷,即如果x=0结果全为1。

    看一下更多的例子:

    `   10111100  (x)
    |   10111011  (x-1)
        --------
        10111111
     
        01110111  (x)
    |   01110110  (x-1)
        --------
        01110111
     
        00000001  (x)
    |   00000000  (x-1)
        --------
        00000001
     
        10000000  (x = -128)
    |   01111111  (x-1 = 127)
        --------
        11111111
     
        11111111  (x = -1)
    |   11111110  (x-1 = -2)
        --------
        11111111
     
        00000000  (x)
    |   11111111  (x-1)
        --------
        11111111

    尽管不能像前一个技巧那样严格,我们还是来简单证明一下(详细论述浪费大家时间,这也不是学术论文不是)。两种情况,从简单的开始:

    1、不存在最右边的值为1的比特位,这种情况x=0,x-1=-1,-1的二进制补码表示为11111111(好像忘了说,我们讨论的数都在8bit范围,也不失一般性),0与11111111或运算结果是11111111(不是想要的结果,但事实很残酷)。

    2、存在最右边的值为1的比特位,我们故计重施,还是分成两部分(和前一个例子一样),计算x-1只影响右边的比特位,将bi位置为0,所有右边的置为1。现在将结果x-1与小做或运算,左边部分的比特位不变,bi位还是1,右边的都变成了1。结果就是最右边的值为1的比特位向右传播了。

    技巧9:隔离最右边值为0的比特位

    y = ~x & (x+1)

    和技巧7刚好相反,找到x最右边的值为0的比特位,将其他位置为0,这一位置为1。例如x=10101011(找到黑体的0),运算结果是00000100。

    看下面更多的例子:

    `   10111100  (x)
        --------
        01000011  (~x)
    &   10111101  (x+1)
        --------
        00000001
     
        01110111  (x)
        --------
        10001000  (~x)
    &   01111000  (x+1)
        --------
        00001000
     
        00000001  (x)
        --------
        11111110  (~x)
    &   00000010  (x+1)
        --------
        00000010
     
        10000000  (x = -128)
        --------
        01111111  (~x)
    &   10000001  (x+1)
        --------
        00000001
     
        11111111  (x = no rightmost 0-bit)
        --------
        00000000  (~x)
    &   00000000  (x+1)
        --------
        00000000
     
        00000000  (x)
        --------
        11111111  (~x)
    &   00000001  (x+1)
        --------
        00000001

    简单证明:假设存在最右边值为0的比特位,那么-x和x+1将这一位变位1,-x和x+1的与运算将左边部分的比特位置为0(-x将x左边部分取反),右边部分运算结果也为0(x+1将x右边部分变位0),因此只剩下bi位为1。

    y = x | (x+1)
    例如x=10100011运算后结果为10100111。
    更多的例子:
    `   10111100  (x)
    |   10111101  (x+1)
        --------
        10111101
     
        01110111  (x)
    |   01111000  (x+1)
        --------
        01111111
     
        00000001  (x)
    |   00000010  (x+1)
        --------
        00000011
     
        10000000  (x = -128)
    |   10000001  (x+1)
        --------
        10000001
     
        11111111  (x = no rightmost 0-bit)
    |   00000000  (x+1)
        --------
        11111111
     
        00000000  (x)
    |   00000001  (x+1)
        --------
        00000001

    事实上,x和x+1进行或运算并不丢失任何信息,将x加1只是填充了最右边的一个0,结果是max{x,x+1}。当x+1溢出时,结果是0,x并没有值为0的比特位,如果没有溢出结果就是x+1,或运算后最右边的0比特被置为1。

    Copyright © 2015 programnote
  • 相关阅读:
    报表设计器的使用之一:入门
    统计图开发之二:点图元
    统计图开发之一:画法定义
    集算器之五:序表
    集算器之四:程序流程
    忏悔录
    请不要离我而去
    所想和所做 所梦和所成
    做出改变,不断改变。
    Linux 操作命令
  • 原文地址:https://www.cnblogs.com/programnote/p/4689303.html
Copyright © 2020-2023  润新知