zoukankan      html  css  js  c++  java
  • 计算机基础——位运算

    位运算操作是由处理器支持的底层操作,因此运行速度很快。尽管现代计算机处理器拥有了更长的指令流水线和更优的架构设计,使得加法和乘法运算几乎与位运算一样快,但是位运算消耗更少的资源。

    你可能经常在JDK源码中看到位运算操作,因此对位运算的掌握是有必要的。

    举个例子,比如java.lang.Long的hashCode()方法:

        public static int hashCode(long value) {
            return (int)(value ^ (value >>> 32));
        }
    

    这里就用到了异或运算 '^' 和逻辑右移运算 '>>>' ,需要注意到这里面有一个运算符优先级的问题。 优先级的问题看这里。

    移位运算符

    Java中移位运算有三种,分别是左移运算、算术右移、逻辑右移,对应的操作符是 '<<' 、 '>>' 和 '>>>'。移位运算符的右操作数需要进行模32的运算,如果是long类型,需要进行模64的运算。例如,1 << 35 就等同于 1 << 3。

    左移运算 '<<'

    左移运算将数字的二进制位全部向高位移动,低位补 0 。例如,数字 23 左移 1 位,即为 23 << 1 ,左移运算的二进制位如下:

                0001 0111 (数字 23 )
    左移 1 位 = 0010 1110 (数字 46 )
    

    可以发现,左移 1 位相当于乘以 2 ,而左移 n 位相当于乘以 2 的 n 次方。需要注意的是,可能会导致溢出。

    算术右移 '>>' 和 逻辑右移 '>>>'

    右移运算与左移运算刚好相反,右移将数字的二进制位全部向低位移动,算术右移高位补符号位,逻辑右移高位补 0 。也就是说,数字 -105 算术右移 1 位,即 -105 >> 1,二进制位如下:

                    1001 0111 (数字 -105 )
    算术右移 1 位 = 1100 1011 (数字 -53 )
    

    同样地,我们发现算术右移 1 位相当于除以 2 ,由于最低位的值被掩盖了,-105 跟 -106 右移结果将是相同的。
    如果是逻辑右移,即 -105 >>> 1,二进制位如下:

                    1001 0111 (数字 -105 )
    逻辑右移 1 位 = 0100 1011 (数字 75 )
    

    算术右移和逻辑右移的区别就在于,算术右移将补充符号位,逻辑右移将补充 0 。

    异或运算 '^'

    异或运算的规则是,相同为 0 ,不同为 1 。例如:

           0101
      XOR  0011
        =  0110
    

    结合已经学习的逻辑右移运算和异或运算,回头来看java.lang.Long的hashCode()方法,会发现作者实现得真巧妙。一个较好的hashCode()实现,应该尽量均匀地映射到 int 范围上。long 类型的 value >>> 32 ,会将高 32 位移到低 32 位,然后高 32 位补 0 。如果这个 long 类型的 value 值没有超过 int 类型的表示范围,value >>> 32 的二进制结果将是 64 个 0。即 value ^ ( value >>> 32) 将转换成 value ^ 0,而与 value ^ 0 等于 value。结果就是,当 long 类型的值不超过 int 类型的表示范围时,Long 的hashCode()方法,将直接返回对应的 int 值。也就是说,被完全均匀地 hash 映射。

    或运算 '|'

    或运算的规则是,有 1 为 1 ,全为 0 时为 0 。例如:

            0101 (数字 5 )
        OR  0011 (数字 3 )
        =   0111 (数字 7 )
    

    或运算可以用来set(1) ,举个例子,二进制数 0010 (数字 2 )通过或运算第四位可以单独被置为 1 :

            0010 (数字 2)
        OR  1000 (数字 8)
        =   1010 (数字 10)
    

    与运算 '&'

    与运算的规则是,都为 1 时为 1,有一个为 0 就为 0 。例如:

            0101 (数字 5)
        AND 0011 (数字 3)
        =   0001 (数字 1)
    

    与运算可以用于 clear(0) 操作,或者对特殊的位 set(1),这里的 set(1) 与或运算是不同的,与运算 set(1) 只会让被设置的位为 1 ,例如:

            0011 (数字 3)
        AND 0010 (数字 2)
        =   0010 (数字 2)
    

    因为结果是 0010 ,我们便知道原数字的第 2 位是 1 。这个操作被称为位屏蔽。

    再举个例子, 假设 0110 (数字 6 )是一个 4 位标记,第 1 位和第 4 位已经被 clear(0),第 2 位和第 3 位被 set(1)。现在可以通过与运算将第 3 位置为 0 :

            0110 (数字 6)
        AND 1011 (数字 11)
        =   0010 (数字 2)
    

    因为这个特性,与运算可以通过校验最低位的值来进行奇偶校验。例如:

            0110 (数字 6)
        AND 0001 (数字 1)
        =   0000 (数字 0)
    

    因为 6 AND 1 等于 0,也就是说 6 能被 2 整除,所以是偶数。

    取反运算 '~'

    取反运算的规则是,将二进制的每一位取反,1 变为 0, 0 变为 1。例如:

        NOT 0111 (数字 7)
        =   1000 (数字 8)
    
        NOT 10101011  (数字 171)
        =   01010100  (数字 84)
    
  • 相关阅读:
    arrow
    简单库函数
    计算机视觉从入门到放肆
    合并排序算法
    React应用数据传递的方式
    发布一个npm package
    绝对路径/相对路径/根路径
    常见的数据处理方法
    从设计稿到实现React应用(分类数据处理)
    提高React组件的复用性
  • 原文地址:https://www.cnblogs.com/bluemilk/p/10528362.html
Copyright © 2011-2022 走看看