⼆进制及其运算学习(原码、反码、补码、位运算)
学习背景:最近在看很多JAVA类的源码,遇到了很多的位运算,所以系统的学习了下有关⼆进制的知识。
⾸先,看⼀下JAVA中的基本数据的字节(Byte)长度和bit长度:
基本数据类型字节Byte bit byte1字节8位
short2字节16位
int4字节32位
long8字节64位
float4字节32位
double8字节64位
boolean1字节(也说4字节)8位
char2字节16位
java是怎么记录数据的
计算机以⼆进制(0和1)来记录数据。在JAVA中根据基本数据类型的长度,⾼位没有记录数则,⾼位补0。⾸位记录数字的正负(0:正数;1:负数)。
⽐如 :
(int)15 : 0000,0000,0000,0000,0000,0000,0000,1111
(int)-15 :1000,0000,0000,0000,0000,0000,0000,1111 (原码)
(long)15: 0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,1111
原码、反码、补码
⾸先了解⼀下计算机的计算原理:在我们平常学的数学中,有+ 、- 、* 、/ 。⽽加减乘除在计算机中都可以⽤加法来记算,这样的计算机的计算逻辑简单,达到⾼效的效果。
减法可以使⽤加法运算,⽐如15-7可以使⽤15+(-7)来计算。
乘法是加法的累积,乘法在计算机运算中分为原码乘法和补码乘法,都是通过加法+位移的⽅式的运算。
除法是减法的累积,除法在计算机运算中也分为原码除法和补码除法,也都是通过加法+位移的⽅式的运算。
原码:⽤符号位和数值表⽰带符号数,正数的符号位⽤“0”表⽰,负数的符号位⽤“1”表⽰,数值部分⽤⼆进制形式表⽰。
举例:
⼗进制⼆进制原码
(int)70000,0000,0000,0000,0000,0000,0000,0111
(int)-31000,0000,0000,0000,0000,0000,0000,0011
想⼀下,如果计算机只⽤原码来计算加法,则势必不能简单的把数字相加,⼀定需要考虑符号的情况,这样每次都得判断符号位的情况会使得计算的效率很低,所以引⼊了 反码 的概念,⽆需考虑符号位的情况,把符号位直接纳⼊到计算中,通过简单的加法完成。
反码:正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。
⼗进制⼆进制原码⼆进制反码
(int)70000,0000,0000,0000,0000,0000,0000,01110000,0000,0000,0000,0000,0000,0000,0111(int)-31000,0000,0000,0000,0000,0000,0000,00111111,1111,1111,1111,1111,1111,1111,1100
我们现在再来通过加法来计算下 : 7+(-3)
计算过程⼆进制
(int)70000,0000,0000,0000,0000,0000,0000,0111 (反码)
(int)-31111,1111,1111,1111,1111,1111,1111,1100(反码)
7+(-3) =0000,0000,0000,0000,0000,0000,0000,0011(反码)
换算成原码0000,0000,0000,0000,0000,0000,0000,0011(原码)
换算成⼗进制3
结果怎么不对呢?还差1,这个时候我们需要下原因,看⼀下特殊的计算:
计算:1-1 等价于 1+(-1)
0000,0000,0000,0000,0000,0000,0000,0001(1的反码)
1111,1111,1111,1111,1111,1111,1111,1110(-1的反码)
1111,1111,1111,1111,1111,1111,1111,1111 (1+(-1)的反码)
1000,0000,0000,0000,0000,0000,0000,0000 (原码)
最后结果为: -0
计算:0+0
0000,0000,0000,0000,0000,0000,0000,0000(0的反码)
0000,0000,0000,0000,0000,0000,0000,0000(0的反码)
0000,0000,0000,0000,0000,0000,0000,0000 (0+0的反码)
1000,0000,0000,0000,0000,0000,0000,0000 (原码)
最后结果为: 0
由此可知:⼆进制使⽤符号位表⽰正负,在计算正数和负数相加的时候,由于符号位参与了计算,多了⼀个【-0】的位数,所以结果会差1;⽽在计算正数相加的时候,由于正数的反码和补码⼀样,符号位并不会实际影响最后的结果,也就是避开了-0这个位数。
那么怎么解决这个问题,由此引⼊了补码。
补码:正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后⼀位加1
⼗进制⼆进制原码⼆进制反码⼆进制补码
(int)70000,0000,0000,0000,0000,0000,0000,01110000,0000,0000,0000,0000,0000,0000,01110000,0000,0000,0000,0000,0000,0000,0111
(int)-
1000,0000,0000,0000,0000,0000,0000,00111111,1111,1111,1111,1111,1111,1111,11001111,1111,1111,1111,1111,1111,1111,1101 3
现在,再来计算⼀下: 7+(-3)
计算过程⼆进制
(int)70000,0000,0000,0000,0000,0000,0000,0111 (补码)
(int)-31111,1111,1111,1111,1111,1111,1111,1101(补码)
7+(-3) =0000,0000,0000,0000,0000,0000,0000,0100(补码)
换算成原码0000,0000,0000,0000,0000,0000,0000,0100(原码)
换算成⼗进制4
计算结果正确。
注意:原码与反码,互为反码;原码与补码,互为补码;
⽐如:
原码转为反码和反码转为原码
原码转反码(符号位不变,其余位取反)反码转原码(同样是符号位不变,其余位取反)1000,0000,0000,0000,0000,0000,0000,0011(原码)1111,1111,1111,1111,1111,1111,1111,1100(反码)
1111,1111,1111,1111,1111,1111,1111,1100 (反码)1000,0000,0000,0000,0000,0000,0000,0011(原码)原码转反码(符号位不变,其余位取反)反码转原码(同样是符号位不变,其余位取反)
原码转为补码和补码转原码
原码转反码(符号位不变,其余位取反,再+1)反码转原码(同样是符号位不变,其余位取反,再+1)1000,0000,0000,0000,0000,0000,0000,0011(原码)1111,1111,1111,1111,1111,1111,1111,1101(补码)1111,1111,1111,1111,1111,1111,1111,1101 (补码)1000,0000,0000,0000,0000,0000,0000,0011(原码)⼆进制的位运算
再来学习⼀下,⼆进制的位运算
⼆进制中的1和0,我们也可以理解为真假,即1:真;0:假。
&: 与
计算规则:有0为0,同时为1才为1
辅助记忆:真真为真 真假为假 假假为假
⼗进制⼆进制原码
(int)70000,0000,0000,0000,0000,0000,0000,0111
(int)30000,0000,0000,0000,0000,0000,0000,0011
7&3 = 30000,0000,0000,0000,0000,0000,0000,0011
|: 或
计算规则:有1为1,同时为0才为0
辅助记忆:真真为真 真假为真 假假为假
⼗进制⼆进制原码
(int)70000,0000,0000,0000,0000,0000,0000,0111
(int)30000,0000,0000,0000,0000,0000,0000,0011
7|3 = 70000,0000,0000,0000,0000,0000,0000,0111
~: ⾮
计算规则:按位取反(包括符号位)
⼗进制⼆进制
(int)70000,0000,0000,0000,0000,0000,0000,0111
~7 = -81111,1111,1111,1111,1111,1111,1111,1000
^: 异或
计算规则:相同为0,不同为1
辅助记忆:真真为假 真假为真 假假为假
⼗进制⼆进制原码
(int)70000,0000,0000,0000,0000,0000,0000,0111
(int)30000,0000,0000,0000,0000,0000,0000,0011
7^3 = 40000,0000,0000,0000,0000,0000,0000,0100
位移运算:使⽤补码位移
<<:有符号左移
规则:丢弃⾼位,低位补0
在数字没有溢出的前提下,对于正数和负数,左移n位就相当于乘以2的n次⽅。
注意“”如果移动的位数超过了该类型的最⼤位数,那么编译器会对移动的位数取模。如对int型移动33位,实际上只移动了33%32=1位
⼗进制⼆进制原码
(int)30000,0000,0000,0000,0000,0000,0000,0011(正数补码 = 原码)
3<<2 = 120000,0000,0000,0000,0000,0000,0000,1100 (正数补码 = 原码)
⼗进制⼆进制原码
(int)-31111,1111,1111,1111,1111,1111,1111,1101(补码)
-3<<2 = -121111,1111,1111,1111,1111,1111,1111,0100 (补码)
-121000,0000,0000,0000,0000,0000,0000,1100 (原码)
左移超过数据类型最⼤位,取模移位。
如图:
3 << 33 等价于:3 << 1
3 << 3
4 等价于:3 << 2
>>:有符号右移
规则:符号位不变,丢弃低位,⾼位补上符号位
正数右移n位相当于除以2的n次⽅(取整)。
负数右移n位相当于除以2的n次⽅(取整 + (-1))。
注意“”如果移动的位数超过了该类型的最⼤位数,那么编译器会对移动的位数取模。如对int型移动33位,实际上只移动了33%32=1位。
在未超出该数据类型的最⼤位数:全部移动出去的情况,整个⾼位使⽤0表⽰,那就是全都是0,结果⾃然为0;负数整个⾼位为1,那就是全都是1,结果⾃然为-1
⼗进制⼆进制原码
(int)70000,0000,0000,0000,0000,0000,0000,0111(正数补码 = 原码)
7>>2 = 10000,0000,0000,0000,0000,0000,0000,0001 (正数补码 = 原码)
⼗进制⼆进制原码
(int)-71111,1111,1111,1111,1111,1111,1111,1001(补码)
-7>>2 = -21111,1111,1111,1111,1111,1111,1111,1110 (补码)
-21000,0000,0000,0000,0000,0000,0000,0010 (原码)
如下是移动位数超出了该数据类型的最⼤位数的情况:
>>>: ⽆符号右移
规则:不论正负,⾼位均补0
注意“”如果移动的位数超过了该类型的最⼤位数,那么编译器会对移动的位数取模。如对int型移动33位,实际上只移动了33%32=1位。
⼗进制⼆进制原码
负数二进制补码运算法则(int)70000,0000,0000,0000,0000,0000,0000,0111(正数补码 = 原码)
7>>>2 = 10000,0000,0000,0000,0000,0000,0000,0001 (正数补码 = 原码)
⼗进制⼆进制原码
(int)-71111,1111,1111,1111,1111,1111,1111,1001(补码)-7>>>2 = 10737418220011,1111,1111,1111,1111,1111,1111,1110 (补码 )10737418220011,1111,1111,1111,1111,1111,1111,1110 (原码)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。