6728: 学习系列 —— 位运算
[Creator : ]
Description
计算机中的进制
二进制 Binary Number
二进制数字,就是底数位 $2$ 的数字,使用 $2$ 个符号 $01$。
int n = 0b1010111100011100; // 44828
十六进制 Hexadecimal Number
十六进制数字,就是底数位 $16$ 的数字,使用 $16$ 个符号 $0123456789abcdef$。
int n = 0xaf1c; // 44828
int n = 0xaf1c; // 44828
位运算概述
位运算就是基于整数的二进制表示进行的运算。由于计算机内部就是以二进制来存储数据,位运算是相当快的。例如:$(5)_{10}=(101)_2, (6)_{10}=(110)_2$
基本的位运算共 $6$ 种,分别为按位与、按位或、按位异或、按位反、左移和右移。
符号 |
描述 |
运算规则 |
& | 按位与 |
两个位都为 $1$ 时,结果才为 $1$。 |
| | 按位或 |
两个位都为 $0$ 时,结果才为 $0$。 |
^ | 按位异或 |
两个位相同为 $0$,相异为 $1$。 |
~ | 按位反 |
$0$ 变 $1$,$1$ 变 $0$。 |
<< | 左移 |
各二进位全部左移若干位,高位丢弃,低位补 $0$。 |
>> | 右移 |
各二进位全部右移若干位,对无符号数,高位补 $0$,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补 $0$(逻辑右移)。 |
按位与
【运算规则】x & y:同为 $1$ 时,结果为 $1$
3 & 4 3(0000 0011) 4(0000 0100) ------------- 0(0000 0000)【作用】
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$ 是不是偶数。
按位或
【运算规则】x | y:同为 $0$ 时,结果为 $0$
3 | 4 3(0000 0011) 4(0000 0100) ------------- 7(0000 0111)【作用】
1)常用来对一个数据的某些位设置为 $1$
比如将数 X=1010 1110 的低 $4$ 位设置为 $1$,只需要另找一个数 Y,令 Y 的低 $4$ 位为 $1$,其余位为 $0$,即Y=0000 1111,然后将 X 与 Y 进行按位或运算(X|Y=1010 1111)即可得到。
按位异或
【运算规则】x^y 相同为 $0$,不同为 $1$
3 ^ 4 3(0000 0011) 4(0000 0100) ------------- 7(0000 0111)【作用】
1)翻转指定位
比如将数 X=1010 1110 的低 $4$ 位进行翻转,只需要另找一个数 Y,令 Y 的低 $4$ 位为 $1$,其余位为 $0$,即Y=0000 1111,然后将 X 与 Y 进行异或运算(X^Y=1010 0001)即可得到。
2)与0相异或值不变
例如:1010 1110 ^ 0000 0000 = 1010 1110
3)交换两个数
void Swap(int &a, int &b){ if (a != b){ a ^= b; b ^= a; a ^= b; } }
按位反
【运算规则】~x $0$ 变 $1$,$1$ 变 $0$
按位取反:二进制每一位取反,0变1,1变0。 ~9的计算步骤: 转二进制:0 1001 计算补码:0 1001 按位取反:1 0110 转为原码: 按位取反:1 1001 末位加一:1 1010 符号位为1是负数,即-10 “~x”的结果为“-(x+1)”
【作用】
1)使一个数的最低位为零 使 a 的最低位为 $0$,可以表示为:a & ~1。~1 的值为 1111 1111 1111 1110,再按"与"运算,最低位一定为 $0$。因为" ~"运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。
左移
【运算规则】左移可看作整数 $m$ 乘以 $2^n$。
3<<4 3(0000 0011)待移位的数字 6(0000 0110)左移第一位,左移一位之后,最右边的将会缺失,所以不足位数,补一位0,最左边将会多出一位,所以舍掉一位 12(0000 1100)左移第二位,最右边再补一位0,最左边再舍掉一位 24(0001 1000)左移第三位,最右边再补一位0,最左边再舍掉一位 48(0011 0000)左移第四位,最右边再补一位0,最左边再舍掉一位 = 3 * 2⁴ = 3 * 16 = 48
右移
右移可看作整数 $m$ 除以 $2^n$。
8 >> 2 8(0000 1000)待右移的数字 4(0000 0100)右移第一位,最左边将会缺失一位,不足位数补一位0,最右边将会多出一位,所以舍掉最右边的一位 2(0000 0010)右移第二位,最左边再补一位0,最右边再舍掉一位 = 8 / 2² = 8 / 4 = 2
备注
以"与运算"为例说明如下:我们假设在某个系统下 C++ 语言中 long 型占 $4$ 个字节,int 型占 $2$ 个字节,如果一个 long 型数据与一个 int 型数据进行"与运算",右端对齐后,左边不足的位依下面三种情况补足,- 1)如果整型数据为正数,左边补 $16$ 个 $0$。
- 2)如果整型数据为负数,左边补 $16$ 个 $1$。
- 3)如果整形数据为无符号数,左边也补 $16$ 个 $0$。 如:long a=123;int b=1;计算a& b。 如:long a=123;int b=-1;计算a& b。
如:long a=123;unsigned intb=1;计算a & b。