Bootstrap

位运算中的移位运算

编程语言中的位运算是相通的,这里我们讨论的是Java中的移位运算。
在Java中有8大基本数据类型,但是只有 byte、short、int、long和char
能够使用位运算。

位运算包括有:按位与、按位或、按位异或、左移运算、右移运算和无符号右移运算。
我们今天讨论后边三种。

在讨论这三种方式之前,我们需要知道一些基础的知识:原码、反码和补码
数据在计算机中是以补码的形式保存的,意味着在进行位运算时使用的是补码计算,这点需要注意。

数据在计算机中是以二进制补码的形式保存,二进制可以细分为无符号数、正数和负数三种。二进制,有符号数,首位为1的是负数,首位为0的是正数。(规定)
无符号数没有正负之分,所以也没有首位的限制。(规定)

我们根据这三种类别来分析原码、反码和补码。

无符号数

无符号数的原码、反码、补码都一样,皆为该数的二进制表示法


举例

1(无符号数)>0000 0001(原码)>0000 0001(反码)==>0000 0001(补码)


正数

正数的原码、反码、补码都一样,皆为该数的二进制表示法(同无符号数差不多)


举例

1==>0000 0001(原码)>0000 0001(反码)>0000 0001(补码)

(首位为0的表示正数,所以原码、反码和补码的首位都是0)

负数

原码定义:负数的原码为该数对应的无符号数的二进制,将首位置1。

举例

-1==>1(无符号数)>0000 0001(无符号数的二进制)>1000 0001(原码,首位置1)。

(负数的首位为1)

反码定义:负数的反码为该数原码的符号位不变,其它位取反。

举例

-1==>1000 0001(原码)==>1111 1110(反码,符号为不变,其它位取反)。

(负数的首位为1)

补码定义:负数的补码为该数对应的无符号数的二进制取反加一。 如果结果首位为0,需置为1

举例

-128==>128(无符号数)>1000 0000(-128的无符号数的二进制)>0111 1111(取反)==>1000 0000(补码,加一)(负数的首位为1)

-1==>1(无符号数)>0000 0001(-1对应无符号数的二进制)>1111 1110(取反)==>1111 1111(补码,加1)(负数的首位为1)

下面我们来讨论移位运算

左移运算 <<

左移运算的口诀是:高位丢弃,低位补0
左移n位就是乘以2的n次方。
例如: 3<<2 就是3乘以2的平方 结果是12
那么这个12是怎么计算来的呢?
3的原码(0000 0011)=》3的补码(0000 0011)-》左移两位(0000 1100)-》12

在来一个负数
例如:-5<<2
计算过程:
-5的原码(1000 0101)-》-5的反码(1111 1010)-》-5的补码(1111 1011)-》左移两位(1110 1100)-》取反(1001 0011)-》取补码(1001 0100)-》-20

右移运算 >>

如果是无符号,高位补0
如果是带符号,正数高位全部补0,负数高位全部补1。

例如:
8 >> 2
计算过程:
8的原码(0000 1000)-》8的补码(0000 1000)-》右移2位(0000 0010)-》2

-5 >>2
计算过程
-5的原码(1000 0101)-》-5的反码(1111 1010)-》-5的补码(1111 1011)-》右移2位(1111 1110)-》取反(1000 0001)-》取补码(1000 0010)-》-2

无符号右移

口诀:忽略符号位,空位都以0补齐
无符号右移运算符与 >> 右移运算符的正数相同,只不过关键在于负数的不同
例如:-4 >>>2

System.out.println(Integer.toBinaryString(-4)); System.out.println(Integer.toBinaryString(-4>>>2));

结果

11111111111111111111111111111100  //-4的补码  int类型 32位
00111111111111111111111111111111  //某个值

得到的结果就很大很大了。

总结

1、无符号右移和右移的区别:
区别在于负数高位补0还是1,无符号负数高位补0,右移高位补1。

2、正数移位运算比较好求,因为正数的原码、反码和补码都一样。直接拿原码计算即可。

3、负数的移位运算需要区别右移和符号右移,高位补1还是0。

4、负数的移位运算需要先将原码转为补码,移位后再逆向取反和补码得到某个值的原码。

;