Java位运算及补码存储

大家都知道,计算机中二进制数值是以补码的形式存储的,那么为什么二进制数值在原码、反码、补码中选择以补码的形式存储呢?

一、原码形式存储

首先,原码是站在用户角度的,是原始的二进制!

求原码步骤:

  1. 用户的数字分为正负数,需要有一位存储符号
  2. 最高位为符号位:0为正,1为负
  3. 左边是高位,右边是低位

由原码的计算方式可以发现源码存储会引发2个问题:

(1)0有两个存储方式

我们以char型(占1字节,8位)为例(下同):

+0:    0000 0000

- 0:    1000 0000

不难发现+0和-0的原码是不一样的,而在计算过程中,+0和-0是没有任何区别的。

(2)正数和负数相加,结果不正确(计算机只会加)

1 - 1 =1 + (-1)

 1:  0000 0001

-1:  1000 0001

          1000 0010  =  -2

很显然1-1=0,而用原码进行计算得出的结果却相差甚远!

二、反码形式存储

既然原码不适合作为计算机的存储方式,人们在解决这个问题的过程中又提出了反码的概念

求反码步骤:

  1. 求原码
  2. 符号为不变,其他位取反
  3. 注意:正数原码、反码一样 !

接下来我们检验一下1 - 1:


  1:    0000 0001

- 1:    1111 1110

             1111 1111 -> 1000 000 ( 转换为原码,因为原码是站在用户角度的 )  =  -0

不难可能出反码已经解决的正负数相加结果不正确的问题

然后我们再检验+0、-0


+0  0000 0000

-0   1111 1111

由此看出,反码并没有解决0有两种形式的问题


三、补码形式存储

在反码的基础上,人们有提出了补码的概念

求补码:

补码为其反码+1


注意:正数的原码、反码、补码都一样!

接下来我们对补码存储进行验证:

    +0: 0000 0000

     -0: 原码:  1000 0000

              反码:  1111 1111

              补码:10000 0000(char占1字节八位,最高位丢弃)= 0000 0000

 可以看出补码解决了+0 -0不一样的问题

    +1: 0000 0001

     -1: 1111 1111 

          1 0000 0000(最高位丢弃)= 0000 0000 = 0

 那么补码也解决了正负数相加结果不正确的问题! 

四、总结:

补码是在原码的基础上为了适应计算机运算一步一步完善而来,原码和反码存储都有一定的弊端,因此计算机采用补码存储!


Java提供的位运算符有:

左移( << )、右移( >> ) 、无符号右移( >>> ) 、位与( & ) 、位或( | )、位非( ~ )、位异或( ^ ),除了位非( ~ )是一元操作符外,其它的都是二元操作符。

1、左移( << )

将5左移2位:5<<2

首先会将5转为2进制表示形式 ( java中,整数默认就是int类型,也就是32位):

0000 0000 0000 0000 0000 0000 0000 0101           然后左移2位后,低位补0

0000 0000 0000 0000 0000 0000 0001 0100           换算成10进制为20


2、右移( >> ) 

5>>2
还是先将5转为2进制表示形式:
0000 0000 0000 0000 0000 0000 0000 0101 然后右移2位,高位补0

0000 0000 0000 0000 0000 0000 0000 0001

3、无符号右移( >>> )

我们知道Java中int类型占32位,既可以一个正数,也可以表示负数。其中最高位是符号位,正数为0,负数为1

例如  -5换算成二进制后为(补码形式:原码取反 + 1):

1111 1111 1111 1111 1111 1111 1111 1011   

我们分别对5进行右移3位、 -5进行右移3位和无符号右移3位:


5>>3      结果是0
-5>>3     结果是-1
-5>>>3   结果是536870911

5换算成二进制: 0000 0000 0000 0000 0000 0000 0000 0101

5右移3位后结果为0,0的二进制为: 0000 0000 0000 0000 0000 0000 0000 0000     (高位补0

 -5换算成二进制: 1111 1111 1111 1111 1111 1111 1111 1011

-5右移3位后结果为-1,-1的二进制为: 1111 1111 1111 1111 1111 1111 1111 1111   (高位补1

-5无符号右移3位后的结果 536870911 换算成二进制: 0001 1111 1111 1111 1111 1111 1111 1111   (高位补0

通过其结果转换成二进制后,我们可以发现,正数右移,高位用0补,负数右移,高位用1补,当负数使用无符号右移时,用0进行补位(自然而然的,就由负数变成了正数了)

注意:这里说的是右移,高位补位的情况。正数或者负数左移,低位都是用0补。

4、位与( & )

5 & 3

5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

-------------------------------------------------------------------------------------

1转换为二进制:0000 0000 0000 0000 0000 0000 0000 0001

位与:第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0

5、位或( | )

5 | 3
5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

-------------------------------------------------------------------------------------

7转换为二进制:0000 0000 0000 0000 0000 0000 0000 0111


位或操作:第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0

6、位异或( ^ )

5 ^ 3
5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101

3转换为二进制:0000 0000 0000 0000 0000 0000 0000 0011

-------------------------------------------------------------------------------------
6转换为二进制:0000 0000 0000 0000 0000 0000 0000 0110


位异或:第一个操作数的的第n位于第二个操作数的第n位 相反,那么结果的第n为也为1,否则为0


7、位非( ~ )           

~5
 5转换为二进制:0000 0000 0000 0000 0000 0000 0000 0101
-------------------------------------------------------------------------------------

-6转换为二进制:1111 1111 1111 1111 1111 1111 1111 1010

位非:操作数的第n位为1,那么结果的第n位为0,反之。

由位运算操作符衍生而来的有:

&= 按位与赋值

|=  按位或赋值

^= 按位非赋值

>>= 右移赋值

>>>= 无符号右移赋值

<<= 赋值左移

和 += 一个概念而已。

参考:

https://blog.csdn.net/qq_41727218/article/details/79521759

https://blog.csdn.net/xiaochunyong/article/details/7748713

原文地址:https://www.cnblogs.com/codestarer/p/13635542.html