位运算小记顺带复习一下原码补码反码

发布时间 2023-09-23 00:00:16作者: Ransang

今天看到一些位运算操作的代码,整个人瞬间宕机了,就抽时间了解了一下,顺便做了点笔记。

位运算符概览

运算符 运算规则
按位与 & 两个操作数同时为1,结果为1
按位或 I 两个操作数只要有一个为1,结果就为1
按位非 ~ 1变0,0变1
按位异或 ^ 两个操作数相同,结果为0,不相同,结果为1
左移 << 二进制数整体左移,高位(左侧)丢弃,低位(右侧)补0
右移 >> 二进制数整体右移,左侧空位补0

位运算是直接操作二进制数的运算,在解释其运算规则时,先来了解一下二机制数的相关概念。

原码 反码 补码

计算机中都是用0和1来表示数据的,以8位二进制数为例0000 0000就代表0,但是这样没办法表示负数,于是就规定最右侧的一位为符号位,0为正,1为负,其他位为真值,这种方法表示的就叫原码

那么计算机在实际运算的时候是直接拿原码来计算的吗,我们先来看一个例子:
1 + 1 = 0000 0001 + 0000 0001 = 0000 0010 = 2
结果好像没错,但是我们先别忙着下结论,再来看下一个例子:
1 - 1 = 1 + (-1) = 0000 0001 + 1000 0001 = 1000 0010 = -2
从这里就看出问题来了,直接使用原码计算会出现错误,经过研究,人们发现对于负数,将其真值部分按位取反,正数不变,这就是反码,也即正数的反码是其原码本身,负数的的反码为除符号位按位取反,再对其进行上述运算就能得出正确的结果,如下:
1 - 1 = 1 + (-1) = 0000 0001原 + 1000 0001原 = 0000 0001反 + 1111 1110反 = 1111 1111反 = 1000 0000原 = -0
但是这种也有问题,-0的负号显然是无意义的,而且此时0的二进制有两种表示方式,这肯定会给一些计算带来问题。于是人们在此基础上继续深入研究,最终提出了补码的概念,即正数的补码是其原码本身,负数的补码为其反码加1,再来看看计算结果:
1 - 1 = 1 + (-1) = 0000 0001原 + 1000 0001原 = 0000 0001补 + 1111 1111补 = 0000 0000补 = 0000 0000原 = 0
这下终于没问题了,所以在计算机内部的二进制表示其实都是用补码来进行的,与此同时人们也规定了1000 0000表示-128,这也解释了为什么有符号8位整型的范围是-128到127。
这里也总结一下:

  • 正数最简单:三码合一
  • 负数的反码:符号位不变,真值按位取反,也即其正数的原码按位取反
  • 负数的补码:其反码加1,也即其正数的原码按位取反后加1

关于这块更深入的研究可以看下这篇文章原码补码反码,里面关于补码的数学原理解释的很清晰,简直不要太强。

在了解了计算机内部使用的是补码这个机制后再看位运算其实就没那么懵逼了。

按位与 &

简单来说就是同1为1,否则为0,比如1 & -1,注意这里负数要用其补码,正数三码合一就无所谓了:
1 & -1 = 0000 0001补 & 1111 1111补 = 0000 0001= 1
与运算的用途:
1)清零
如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。
2)取一个数的指定位
比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。
3)判断奇偶
只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

按位或 |

简单说就是有1为1,否则为0,负数记得还是用补码哦。