Java中左移、右移、无符号右移

计算机对有符号数(包括浮点数)的表示有三种方法:原码、反码和补码,补码=反码+1。在 二进制里,是用 0 和 1 来表示正负的,最高位为符号位,最高位为 1 代表负数,最高位为 0 代表正数。

在计算机中对数进行操作,移位操作可能比乘除操作更效率更高。比如,在ArrayList的扩容实现机制中,Java8中就将0.5倍由原来的除以2改为右移1位,以提高效率。

Java中负数的表示方法

在讲解移位操作之前,我们有必要了解一下Java中的负数表示方法。

绝对值取反码再加一

以Java中8位的byte为例,最大值为:01111111,最小值为1000 0000。

那么根据十进制的数字,我们如何转换为二进制呢?对于正数我们直接转换即可,对于负数则有一个过程。

以负数-5为例:

  1. 先将-5的绝对值转换成二进制,即为00000101;
  2. 然后求该二进制的反码,即为1111 1010;
  3. 最后将反码加1,即为:1111 1011

所以Java中Integer.toBinaryString(-5)结果为11111111 11111111 1111111111111011(Integer是32位(bit)的)

左移

正负数皆右补0

System.out.println(-3<<1);左移相对来说比较简单.

1111 1111 1111 1111 1111 1111 1111 1101左移一位为

1111 1111 1111 1111 1111 1111 1111 1010,其为-6的补码.

System.out.println(Integer.MAX_VALUE<< 1);输出为-2

右移

正数左补0、负数左补1

System.out.println(-3>>1);结果是-2,为什么会是-2呢?下面我们来看一下.

System.out.println(Integer.toHexString(-3));得到-3的16进制为fffffffd(此为-3的补码,计算机中负数用补码表示).

转换成2进制为1111 1111 1111 1111 1111 1111 1111 1101

右移一位为1111 1111 1111 1111 1111 1111 1111 1110,显而易见此为-2补码.

无符号右移

全部左补0

System.out.println(-3>>>1);

1111 1111 1111 1111 1111 1111 1111 1101无符号右移,高位补0,

01111 1111 1111 1111 1111 1111 1111 1110,其为2147483646的原码.

为什么没有无符号左移?

因为左移是在后面补0,而右移是在前面边补1或0

有无符号是取决于数的前面的第一位是0还是1,所以右移是会产生到底补1还是0的问题。

而左移始终是在右边补,不会产生符号问题。所以没有必要无符号左移<<<。

无符号左移<<<和左移<<是一样的概念

Java中表示二进制、八进制、十进制、十六进制

Java里不能这样表示二进制,只能是 8,10,16进制

  • 8进制:前置0
  • 10进制:不需要前置
  • 16进制:前置0x或者0X

为什么8位的二进制补码范围是[-128,127]

计算机对带符号数的表示有三种方法:原码、反码和补码

8位原码和反码能够表示数的范围是-127~127

8位补码能够表示数的范围是 -128~127

所以既然范围是-128~127,那肯定是用补码表示的。

10000000-11111111表示-128到-1, 00000000-01111111表示0-127

补码的1111 1111转换成原码就是1000 0001,也就是-1。

补码就是二进制表示负数的一种方法

负数的补码就是对反码加一

正数不变,正数的原码反码补码是一样的

在补码中用(-128)代替了(-0),所以补码的表示范围为:(-128 ~ 0 ~ 127)共256个。

注意:(-128)没有相对应的原码和反码, (-128) = (10000000)

为了充分利用资源,就将原来本应该表示“-0”的补码规定为代表-128

所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。

反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。