一、计算机基础知识
在介绍位运算之前,先复习一下计算机基础知识:数据类型的表示及其编码
1.编码
因为计算机系统采用二进制表示和处理数据信息,所以计算机中的数值都需要二进制表示。将非二进制形式表示的数值型数据转化为二进制形式表示,使其最终能够被计算机存储和处理,这一过程就是编码,字符就是通过编码转化为二进制。
2.机器数和真值
把一个数连同其符号在机器中的表示加以数值化,这样的数称为机器数。一般用最高有效位来表示数的符号,正数用0表示,负数用1表示。而这个数本身,称为该数的真值,真值的正负号用+和-表示的。
机器数的表示位数与计算机操作系统的位数直接相关。
32位操作系统 机器数也为32位
64位操作系统 机器数也为64位
举例
真值 +13(十进制) 对应 1101(二进制)
则对应的8位/16位操作系统,其机器数为 0000 1101/0000 0000 0000 1101
真值 -13(十进制) 对应 -1101
则对应的8位/16位操作系统,其机器数为 1000 1101/1000 0000 0000 1101
3.无符号数和有符号数
无符号数:不需要考虑符号时,所有的二进制数位表示数的大小。这样,8位的二进制数能表示的最小值是0,最大值是2^8 -1 = 255;
有符号数:用最高位表示符号,用剩余的位表示绝对值,此时为带符号数。这样8位二进制数表示的最小数是 - 2 ^ 7 = -128,最大值是2 ^ 7 - 1 = 127;
有符号数为了简化计算,又引入了下面的概念。
4.原码、反码、补码
其实我们首先要注意的是,原码、反码、补码都是机器数的具体表现形式。这样它们都符合机器数的基本特点,就是最高位为符号位,0表示正数,1表示负数;其余位是数值位。数值位是原码、反码、补码这三者的不同之处。
原码的数值位 与真值的数值位相同;
反码的数值位 原码的数值位取反;
补码的数值位 正数的数值位与原码相同,负数的数值位为反码的数值位加1。
举例
真值 | 原码 | 反码 | 补码 |
---|---|---|---|
+13 | 0000 1101 | 0111 0010 | 0000 1101 |
- 13 | 1000 1101 | 1111 0010 | 1111 0011 |
原码的特点是表示简单直观,与真值转换方便,但不适合运算;
反码就是为了由原码计算补码方便;
补码运算方便。
计算机中通常是使用补码的形式来表示一个数,因为补码运算可以连同符号位一起参与运算。
补码简化了计算机基本运算电路,使加减法都只需要通过加法电路实现,也就是让减去一个正数或加上一个负数这样的运算可以用加上一个正数来代替。于是改变负数存储的形式,存储成一种可以直接当成正数来相加的形式,这种形式就是补码。
关于上述文字的具体解释,感兴趣的读者可以参考这个链接里的内容
计算机中整数为什么以「补码」的形式存储?
二、Java的位运算
位运算是针对二进制的每一位进行运算,它是专门针对数字0和1进行的操作。程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算既可以节约内存,同时使程序速度更快效率更高。
Java中可以进行位运算的类型有 long, int, short, byte, char;但在实际运算中, byte、short、char先转换为长度为32位的 int 类型,然后进行位运算的, long长度为64位可以直接进行位运算,所以 int 和 long 是可以直接进行位运算的
Java位运算符又可以分为 1.逻辑运算符 和 2.位移运算符
1.逻辑运算符有:按位与 &、按位或 |、取反 ~、按位异或 ^
2.位移运算符有:左移 <<、右移 >>、无符号右移 >>>
i. 逻辑运算
需要注意的是,符号位是参与运算的!
1.&(按位与运算) 只要有一个为0,就为0,如 0001 & 0100 结果为 0000
2. |(按位或运算) 只要有一个为1,就为1,如 0001 & 0100 结果为 0101
3. ~(取反运算) 这个只对一个数据进行操作,0取反为1,1取反为0;
4. ^ (按位异或运算) 不同为1,相同为0, 如 0001 ^ 0100 结果为 0101
异或运算的两个比较重要的特点:
1.任何数与自己异或均为0
2.任何数与0异或均为它自己
ii. 位移运算
需要注意的是,都是从最低位开始移动(左移和右移均是如此)
1.左移规则<<:符号位不变,丢弃最高位,0补最低位 (数学意义:如果没有溢出,对于正负数都是进行乘2)
2.右移规则>>:符号位不变,左边补上符号位(数学意义:如果没有溢出,对于正负数都是进行除2)
3.无符号右移规则>>>:忽略了符号位扩展,0补最高位(所以无符号右移对正数毫无意义,只对负数有意义,且连符号位也跟着一起移动)
注:移位运算比乘除运算快!