Java位运算及补码存储
大家都知道,计算机中二进制数值是以补码的形式存储的,那么为什么二进制数值在原码、反码、补码中选择以补码的形式存储呢?
一、原码形式存储
首先,原码是站在用户角度的,是原始的二进制!
求原码步骤:
- 用户的数字分为正负数,需要有一位存储符号
- 最高位为符号位:0为正,1为负
- 左边是高位,右边是低位
由原码的计算方式可以发现源码存储会引发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 - 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位,高位补00000 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 结果是5368709115换算成二进制: 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 01013转换为二进制: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 01013转换为二进制: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 01013转换为二进制: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,反之。
由位运算操作符衍生而来的有:
&= 按位与赋值
|= 按位或赋值
^= 按位非赋值
>>= 右移赋值
>>>= 无符号右移赋值
<<= 赋值左移
和 += 一个概念而已。
参考: