详解位移运算符

正负数的<<, >>和>>>区别

看完本帖后,出了无符号右移的负数情况不能口算之外,算数能力好的可以口算基本的位移结果。由于对位移运算总是搞不清楚,所以小编对各种情况都进行了详细分析,总结了一下来应付面试

转载请注明出处:https://blog.csdn.net/qq_36459257/article/details/80257267

一:左移运算符

正数的左移:
比如2<<3就等价于2*2^3,`
代码:

  int i=2<<3;
  System.out.println(i);
  System.out.println(Integer.toBinaryString(i));`
  //运行结果
  16
  10000

到这里注意到一个规律,我们都知道2的2进制是10,4的是100,8的是1000,16的又是10000…………所以左移几位就是把二进制向左移动几位,空位补齐0而已。
对应的3的二进制为11,3<<3为11000也就是十进制的3*2^3=24。

 int i=3<<3;
 System.out.println(i);
 System.out.println(Integer.toBinaryString(i));
 //运行结果:
   24
   11000

当然了,既然是int类型,应该占32位,高位都是0,只不过这里的运行结果省略掉了而已。比如24应该是00000000 00000000 00000000 00011000。

负数的左移:
首先说明正数和负数的二进制表示的区别就是负数的二进制最高位吧正数的0换成1
比如:

3的原码为:   00000000 00000000 00000000 00000011
-3的原码就是:10000000 00000000 00000000 00000011
-3 << 3  为:10000000 00000000 00000000 00011000

你可以发现规律,就是向左移动了三位,只不过最高位为1而已。

但是在java中,负数的二进制是用补码表示的也就是下面的代码:

 int a=-3;
 int i=-3<<3;
 System.out.println(a);
 System.out.println(Integer.toBinaryString(a));

 System.out.println(i);
 System.out.println(Integer.toBinaryString(i));
 
//运行结果:
-3
11111111111111111111111111111101
-24
11111111111111111111111111101000

到这里会发现,没什么区别,3左移3位是24,-3左移3位是-24。

这里还发现补码就是原码的取反加一**(最高位不变):**
比如-3:

原码:10000000  00000000  00000000  00000011
反码:11111111  11111111  11111111  11111100
补码:11111111  11111111  11111111  11111101

二:右移运算符

正数的右移
右移的原理和左移一样,只不过一个是乘一个是除
比如:16>>3=2
是不是有疑问,我为什么不举上面3>>3位的例子,这样看起来可以对比着学,这种情况后面会解释。

int a=16;
int i=16>>3;
System.out.println(a);
System.out.println(Integer.toBinaryString(a));

System.out.println(i);
System.out.println(Integer.toBinaryString(i));

//运行结果:
16
00000000 00000000 00000000 00010000
2
00000000 00000000 00000000 00000010

看到了吗?就是将原码右移3位前面补齐0就ok了

负数的右移
原理同负数的左移

int a=-16;
int i=-16>>3;
System.out.println(a);
System.out.println(Integer.toBinaryString(a));

System.out.println(i);
System.out.println(Integer.toBinaryString(i));

//运行结果(前面已经讲过java中的负数是用补码表示的):
-16
11111111111111111111111111110000
-2
11111111111111111111111111111110

注意:在补码和原码的切换过程中,无论是补码转换成原码,还是原码转换为补码,都是先取反再加一。
比如:-2
原码转补码

原码:       00000000 00000000 00000000 00000010
取反(反码):11111111 11111111 11111111 11111101
加一(补码):11111111 11111111 11111111 11111110

补码转原码:

补码:       11111111 11111111 11111111 11111110
取反(反码):00000000 00000000 00000000 00000001
加一(原码):00000000 00000000 00000000 00000010

右移不够*位怎么办?
就比如3>>3:

000000000 00000000 00000000 00000010
该原码移动两位就足以将10移除,那第三位怎么移?前面补0。
所以右移某位不够的就补零。就是0,
比如5>>4 “运算5/16”(但是和5/16不一样,5/16是直接取整,而右移运算是向下取整)
,凡是被除数没有除数大的结果都为0

负数的话被除数没有除数大的结果都为-1
比如:-3>>3

原码:10000000 00000000 00000000 00000010
反码:11111111 11111111 11111111 11111101
补码:11111111 11111111 11111111 11111110

由于负数左右移动是操作的补码,所以-3右移三位补码为
补码:11111111 11111111 11111111 11111111
反码:10000000 00000000 00000000 00000000
原码:10000000 00000000 00000000 00000001
所以为-1
-3>>3=-1

之所以得到0和-1就是因为右移运算符本身向下取整的特点,所以零点几结果就是0,负的零点几就是-1
其他的也一样,比如

 int a=-31;
 int i=-31>>3;
 double j=-31/8;
 System.out.println(a);
 System.out.println(Integer.toBinaryString(a));

 System.out.println(i);
 System.out.println(Integer.toBinaryString(i));
 System.out.println(j);

在上面的代码中-31.0/8是-3.875,右移运算向下取整所以为-4,如果运算结果为-3.275结果也是-4

运行结果:

-4
-31
11111111111111111111111111100001
-4
11111111111111111111111111111100
-3.875

三:无符号右移

正数的无符号右移
由于正数的补码和原码没区别所以无符号右移和右移一样,都是补零,所以只考虑负数的。

负数的无符号右移
直接看例子

 int i=-16>>>3;
 int j=-16>>3;
 System.out.println(i);
 System.out.println(Integer.toBinaryString(i));
 System.out.println(j);
 System.out.println(Integer.toBinaryString(j));

运行结果:

536870910
11111111111111111111111111110//这里少了三位,没错是前面补了3个0,真实应为下面这个:
00011111111111111111111111111110
-2
11111111111111111111111111111110

从结果当中看出无符号右移就是不管是正数还是负数,都是补零。所以当负数进行位运算的时候会和想象的有很大的差别。反正这个我是口算不了,如果你们有什么办法教教我,嘻嘻。
上个例子中的536870910的补码前三个被补了零导致补码换为原码的时候数据大了很多。

发现问题请在下方留言,感谢!

你可能感兴趣的:(基础知识)