Skip to content

Latest commit

 

History

History
168 lines (54 loc) · 7.92 KB

位运算符的概念和性质.md

File metadata and controls

168 lines (54 loc) · 7.92 KB

位运算符的概念和性质

位运算概述

计算机采用的是二进制,二进制包括两个数码:0,1。在计算机的底层,一切运算都是基于位运算实现的。

位运算共有 6 种,分别是:与、或、异或、取反、左移和右移,其中左移和右移统称移位运算,移位运算又分为算术移位和逻辑移位。上述位运算中,只有取反是一元运算,其余的都是二元运算。

与、或、异或和取反

与运算的符号是 &,运算规则是:对于每个二进制位,当两个数对应的位都为 1 时,结果才为 1,否则结果为 0。

img

或运算的符号是 ∣,运算规则是:对于每个二进制位,当两个数对应的位都为 0 时,结果才为 0,否则结果为 1。

img

异或运算的符号是 img(在代码中用 表示异或),运算规则是:对于每个二进制位,当两个数对应的位相同时,结果为 0,否则结果为 1。

img

取反运算的符号是 img,运算规则是:对一个数的每个二进制位进行取反操作,0 变成 1,1 变成 0。

img

以下例子显示上述四种位运算符的运算结果,参与运算的数字都采用有符号的 8 位二进制表示。

46 的二进制表示是 00101110,51 的二进制表示是 00110011。考虑以下位运算的结果。

  • 46 & 51 的结果是 34,对应的二进制表示是 00100010。
  • 46 ∣ 51 的结果是 63,对应的二进制表示是 00111111。
  • 46⊕51 的结果是 29,对应的二进制表示是 00011101。
  • 46∼46 的结果是 −47,对应的二进制表示是 11010001。
  • 51∼51 的结果是 -52,对应的二进制表示是 11001100。

移位运算

移位运算按照移位方向分类可以分成左移右移,按照是否带符号分类可以分成算术移位逻辑移位

左移运算的符号是 <<。左移运算时,将全部二进制位向左移动若干位,高位丢弃,低位补 0。对于左移运算,算术移位和逻辑移位是相同的。

右移运算的符号是 >>。右移运算时,将全部二进制位向右移动若干位,低位丢弃,高位的补位由算术移位或逻辑移位决定:

  • 算术右移时,高位补最高位;
  • 逻辑右移时,高位补 0。

以下例子显示移位运算的运算结果,参与运算的数字都采用有符号的 8 位二进制表示。

29 的二进制表示是 00011101。29 左移 2 位的结果是 116,对应的二进制表示是 01110100;29 左移 3 位的结果是 -24,对应的二进制表示是 11101000。

50 的二进制表示是 00110010。50 右移 1 位的结果是 25,对应的二进制表示是 00011001;50 右移 2 位的结果是 12,对应的二进制表示是 00001100。对于 0 和正数,算术右移和逻辑右移的结果是相同的。

-50 的二进制表示是 11001110。-50 算术右移 2 位的结果是 -13,对应的二进制表示是 11110011;-50 逻辑右移 2 位的结果是 51,对应的二进制表示是 00110011。

右移运算中的算术移位和逻辑移位是不同的,计算机内部的右移运算采取的是哪一种呢?

对于C/C++ 而言,数据类型包含有符号类型和无符号类型,其中有符号类型使用关键字 signed 声明,无符号类型使用关键字unsigned 声明,两个关键字都不使用时,默认是有符号类型。对于有符号类型,右移运算为算术右移;对于无符号类型,右移运算为逻辑右移。

对于 Java 而言,不存在无符号类型,所有的表示整数的类型都是有符号类型,因此需要区分算术右移和逻辑右移。在Java 中,算术右移的符号是 >>,逻辑右移的符号是 >>>。

移位运算与乘除法的关系

观察上面的例子可以看到,移位运算与乘除法有密切的关联性。由于计算机的底层的一切运算都是基于位运算实现的,因此使用移位运算实现乘除法的效率显著高于直接乘除法的效率。

左移运算对应乘法运算。将一个数左移 kk 位,等价于将这个数乘以 img。例如,29 左移 2 位的结果是 116,等价于 img。当乘数不是 22 的整数次幂时,可以将乘数拆成若干项 2 的整数次幂之和,例如,img 等价于 (a<<2)+(a<<1)。对于任意整数,乘法运算都可以用左移运算实现,但是需要注意溢出的情况,例如在 8 位二进制表示下,29 左移 3 位就会出现溢出。

算术右移运算对应除法运算。将一个数右移 k 位,相当于将这个数除以 img。例如,50 右移 2 位的结果是 12,等价于 50/4,结果向下取整。

从程序实现的角度,考虑程序中的整数除法,是否可以说,将一个数(算术)右移 k 位,和将这个数除以 img等价?对于 0 和正数,上述说法是成立的,整数除法是向 0 取整,右移运算是向下取整,也是向 0 取整。但是对于负数,上述说法就不成立了,整数除法是向 0 取整,右移运算是向下取整,两者就不相同了。例如,(−50)>>2 的结果是 -13,而 (−50)/4 的结果是 -12,两者是不相等的。

因此,将一个数(算术)右移 k 位,和将这个数除以 img是不等价的。

位运算的性质

位运算的性质有很多,此处列举位运算中的与、或、异或和取反的常见性质。假设以下出现的变量都是有符号整数。

  • 幂等律:img(注意异或不满足幂等律);

  • 交换律:img

  • 结合律:img

  • 分配律:img

  • 德·摩根律:img

  • 取反运算性质:img

  • 与运算性质:img

  • 或运算性质:img

  • 异或运算性质:img

  • 其他性质:

    • img 的结果为将 img 的二进制表示的最后一个 1 变成 0;
    • img(与 img 等价)的结果为只保留 img 的二进制表示的最后一个 1,其余的 1 都变成 0。

利用上述性质,可以巧妙地解决很多位运算的题目。