在深入了解各种位运算之前,需要先了解补码的概念。
一、补码
原码:计算机中对数字的二进制定点表示方法,这种表示方法在数字前面加上一个符号位,“1”代表这个数是负数,“0”代表这个数是正数,除符号位之外,其余位表示该数字的值。(注意:如果明确定义为无符号整数,那么将不存在符号位,本文主要讲述的是有符号整数的情况)
先举一个简单的例子:假设一个数在内存中占8个比特位,那么在内存中一个数就用八个二进制位来表示,“1”用原码来表示就是 00000001,“-1”用原码来表示就是10000001。
这里就出现一个问题,如果用原码来表示“0”的话,会出现+0和-0两种情况,明显不可行,因此计算机中一律是用补码来表示和存储的,与原码相比,使用补码可以将符号位和数值域统一处理,加法和减法也可以统一处理。那补码是什么?
在一个时钟中,时针9要变成6,可以表示为9-3,也可以表示为9+9=12+6(12为一圈)。对于计算机来说,一个数也是有一个有效范围的,在该范围内可以把加减操作都转化为加法操作,那么对于减法来说就需要有一个数补上加减之间的数差,这个数就是补数,对于计算机内部来说,这就是补码。
求补码有三种情况:正整数、负整数、零。
先看第一种情况--正整数:
(假设一个数用八个二进制位来表示)对于一个正整数来说,它的补码和原码是完全相等的,“1”的补码也是00000001,“0”的补码也是00000000。
第二种情况--负整数:
(假设一个数用八个二进制位来表示)对于一个负整数来说,它的补码等同于该负整数的原码除符号位之外的所有位进行取反加一,也就是对后七位取反再加一,例如:对于数字“-1”来说,它的原码是10000001 ,取反加一之后为11111111,所以"-1"的补码就是11111111。
第三种情况--零:
(假设一个数用八个二进制位来表示)“0”用原码表示有两种方式:“00000000”和“10000000”,用补码表示只有唯一的情况,因为“10000000”转换为补码为“100000000”,而在这里一个数只能用八个二进制位表示,所以首位的1被丢弃,因此“0”的补码为“00000000”。
那么已知一个十进制数的补码怎么求出这个数呢?对这个补码再求补码,得出的二进制数转换为十进制。
例如:一个数的补码为“00001010”,该补码的补码是它本身,所以二进制转换为十进制为2^1+2^3=10;一个数的补码为“11110111”,从符号位可知这个数是负数,所以该补码的补码是“10001001”,转换为十进制(符号位不计)为-(2^0+2^3)=-9。
二、位运算
对于任何程序中的数据在计算机内存都是以二进制的形式来存储的,如果我们需要对一个整数的二进制位进行运算操作,就需要用到位运算了。通用的位运算有六种,下面将一一介绍。(以下例子均假设一个数用8个二进制位表示且为有符号整数)
1、按位与运算 &
A&B:把A、B的二进制的每一位进行比较,相同且为1则为1,不同则为0,返回一个二进制结果。这通常用于二进制数的取位操作,例如C&1就可以知道C的第一个二进制位是0还是1。
例子:1)5&3
2)-3&5
2、按位或运算 |
A|B:把A、B的二进制位的每一位进行比较,相同且为0则为0,其余情况为1,返回一个二进制结果。通常用于无条件赋值,例如C|1 返回的结果就是C+1的值。
例子:1)3 | 5
2)-3 | 5
3、按位取反运算 ~
~A:把A的每一个二进制位都进行取反,1变成0,0变成1,返回一个二进制结果。
例子:1)~5
2)~ -3
4、按位异或 ^
A^B:把A、B的每个二进制位进行比较,相同为0,不同为1,返回一个二进制数。两次异或会变成原来的值(A^B)^B=A,故异或可用于一般的加密。
例子:1)5 ^ 3
2)5 ^ -3
5、左移运算符 <<
A<<n:将A的每一个二进制位左移n位,并在右边补n个零,相当于是A乘以2的n次方,等价于A=A*(2^n),但左移的运算速度较快。
例子:1)4<<2
2)-3<<2
6、右移运算符 >>
A>>n:将A的每一个二进制位右移n位,并在左边补n个0(正数)或者n个1(负数),等价于A=A/(2^n),但右移的运算速度较快。需要注意的是,使用右移运算符要在数据不丢失的情况下使用,如果右移到有效位全为0了再右移就没有意义了。
例子:1)4>>2
2)-8>>2
至此,位运算的知识点全部完毕