目录
一 编码方式
1.1 机器数
1.2 真值
1.3 原码
1.4 反码
1.5 补码
二 位运算
2.1 & 与运算
2.2 | 或运算
2.3 ^ 异或运算
2.4 ~ 取反运算
2.5 << 左移运算
2.6 >> 右移运算
2.7 >>> 无符号右移运算(逻辑右移)
2.8 <<、>>、>>>位运算注意点
符号位:0表示正数;1表示负数。
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1。
例如:假设计算机字长为8位,十进制中的数+3 ,转换成二进制就是 0000 0011。如果是-3 ,就是 1000 0011 。
这里的 0000 0011 和 1000 0011 就是机器数。
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。
例如上面的有符号数 1000 0011,其最高位1代表负,其真正的数值是 -3,而不是形式值131(1000 0011 转换成十进制等于131)。
所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例:0000 0001 的真值 = +000 0001 = +1
1000 0001 的真值 = –000 0001 = –1
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。
假设计算机字长是8位的二进制:
因为第一位是符号位, 所以8位二进制数的取值范围就是:[1111 1111 , 0111 1111]
即[-127 , 127] (这和我们已知的[-128,127]不一样,因为计算机采用的编码方式是后面讲的补码)。
原码是人脑最容易理解和计算的表示方式。
反码的表示方法是:
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值,通常要将其转换成原码再计算。
补码的表示方法是:
已知一个数的补码,求原码的操作,有2种方法:
对于负数, 补码表示方式也是人脑无法直观看出其数值的,通常也需要转换成原码再计算其数值。
对于0来说
假设计算机字长为8位,在原码和反码的运算中,运算结果为0,可能会出现[+0](0000 0000)和[-0](1000 0000)这2种结果,有2个编码可以表示0。而在补码运算中,运算结果为0的补码是 0000 0000 ,不会出现 1000 0000 的情况。而-128 = (-1) + (-127),得到的结果补码刚好等于 1000 0000 ,所以将1000 0000 看作是8位字长的最低数,这样就刚好处理了0的2个编码问题和可以多出一个表示最低数。
在计算机编码中,规定0的补码是 0000 0000补 ,1000 0000补表示8位字长的最低数 -128。所以8位字长可以表示的范围为:[-128,127] 。
低字长转为高字长,补码左侧补符号位到满足高字长的位数。
例如
8位字长补码 0000 0010补
16位字长补码 0000 0000 0000 0010补
8位字长补码 1111 1110补
16位字长补码 1111 1111 1111 1110补
两个数相同位置的比特位进行与运算,若两个比特位均为1,则结果就为1,否则为0。
操作数1 | 0 | 0 | 1 | 1 |
操作数2 | 0 | 1 | 0 | 1 |
结果 | 0 | 0 | 0 | 1 |
两个数相同位置的比特位进行与运算,若两个比特位均为0,则结果就为0,否则为1。
操作数1 | 0 | 0 | 1 | 1 |
操作数2 | 0 | 1 | 0 | 1 |
结果 | 0 | 1 | 1 | 1 |
两个数相同位置的比特位进行与运算,若两个比特位相同,则为0,若不相同,则为1。
0和任何数进行异或运算,结果都是任何数本身。
操作数1 | 0 | 0 | 1 | 1 |
操作数2 | 0 | 1 | 0 | 1 |
结果 | 0 | 1 | 1 | 0 |
将数的比特位取反。
操作数1 | 0 | 1 |
结果 | 1 | 0 |
对补码~取反位运算公式(注意:该公式是针对补码进行~取反位运算才适用):将补码表示的数字取反(正数变负数,负数变正数),然后减1。
假设计算机字长为8位,
5的补码:0000 0101补,进行~取反运算,得到结果为-6(1111 1010补)
-5的补码:1111 1011补,进行~取反运算,得到结果为4(0000 0100补)
将一个数表示的二进制向左移n位,符号位和其他位一样要移动,移出的部分将被抛弃,右侧低位补0,符号位可能会发生变化(因为符号位被移出抛弃,新的符号位由右侧的左移替上)。
假设计算机字长为8位,
5的补码:0000 0101补
向左移2位,末位补0
结果补码为:20:0001 0100补
在数据没溢出时, 5 * 2^n(n表示左移n位) = 20
-5的补码:1111 1011补
向左移2位,末位补0
结果补码为:-20:1110 1100补
在数据没溢出时, (-5) * 2^n(n表示左移n位) = -20
-5的补码:1111 1011补
向左移5位,末位补0
结果补码为:96:0110 0000补
-5变成96,负数变成正数。
注意:当int类型的数据进行左移的时候,当左移的位数大于等于32位(int类型的位数为32位)的时候,位数会先求余数(左移的位数对32取余),然后用该余数进行左移,也就是说,如果左移32位的时候,会先进行位数求余数,即为左移32位相当于左移0位 ,所以左移 33 的值和左移一位是一样的,同理右移操作也是如此。
将一个数表示的二进制向右移n位,移出的部分将被抛弃,左侧高位补符号位。例如正数左侧补0,负数左侧补1。
假设计算机字长为8位,
5的补码:0000 0101补
向右移2位,左侧补符号位
结果补码为:1:0000 0001补
-5的补码:1111 1011补
向右移2位,左侧补符号位
结果补码为:(-2)1111 1110补
将一个数表示的二进制无符号向右移n位,移出的部分将被抛弃,无论是正数,还是负数,左侧高位都补0。
假设计算机字长为8位,
5的补码:0000 0101补
无符号向右移2位,左侧补0
结果补码为:1:0000 0001补
-5的补码:1111 1011补
无符号向右移2位,左侧补0
结果补码为:(62)0011 1110补
注意点:在Java中,如果操作数的类型是byte、char、short,利用<<、>>、>>>位运算符进行运算时,Java会将操作数的类型转换为int类型,也就是将操作数表示的二进制的位数补齐到32位(左侧高位补符号位)。
所以在java中,-5 >>> 2 运算的结果不是我们上面(上面是假设计算机字长为8位)算出来的 (62)0011 1110补,因为-5和2都是int类型,字长为32位,所以实际表示二进位为
-5的32位补码:
1111 1111 1111 1111 1111 1111 1111 1011补
无符号向右移2位,左侧补0
0011 1111 1111 1111 1111 1111 1111 1110补
得到的结果为1073741822
如果在Java将-5定义为byte类型,也就是之前说的8位字长,在>>>位运算符中,将byte类型转换为int类型,然后进行位运算,运算结果后在赋值给byte,Java会截取无符号右移后得到的32位二进制的低8位赋值给byte,也就是
0011 1111 1111 1111 1111 1111 1111 1110的1111 1100,所以结果为-2。
如下
参考
运算符>>和>>>有什么区别以及原码、反码、补码_云计算&大数据的博客-CSDN博客_> >>
Java中Short、Byte类型无符号右移>>>原理解析_DarkNames的博客-CSDN博客
JAVA:byte和int类型的转换-原码反码补码_哑巴湖小水怪的博客-CSDN博客
关于java中byte无符号右移 >>>_zxk1995的博客-CSDN博客_byte右移
Java中的<<、>>、>>>运算符_Archie_java的博客-CSDN博客_java<<运算符