通过原码、反码、补码彻底搞清左移、右移、无符号右移

原码、反码、补码

正数
正数的原码、反码、补码都是该数字的二进制表示。
首先我们需要知道,计算机中的移位运算都是以二进制补码形式进行的,就像我们的Integer.toBinaryString(num),其转化出来的数字就是补码的形式。在计算机中是没有正负号的,正数用0表示,负数用1表示。像int,我们知道他是占32位的,但是他的最大数是2^31 - 1,原因就是他有一个是符号位。下面先看几个极限值

System.out.println((1<<31) - 1); //2147483647
System.out.println(1<<31); //-2147483648
int m = -2147483648;
System.out.println(Integer.toBinaryString(m)); //10000000000000000000000000000000

由于int型在日常中比较常用,下面就以int型进行讲解。
这里以55进行举例,为了好理解,我以8位进行讲解

int a = 55;
System.out.println(Integer.toBinaryString(a)); //110111

55的原码是00110111,其中符号位是前面的0,其反码和补码也是00110111。
负数
负数的原码是该数字的二进制表示,反码是符号位不变数值位取反,补码是符号位不变数值位在反码的基础上加1
我们以-83进行讲解,这里也是用8位进行表示,其实前面还有24位1。

int n = -83;
System.out.println(Integer.toBinaryString(n)); //11111111111111111111111110101101

可以看出,输出的是10101101,这里是补码的形式,其中高位的1是符号位。
-83的原码是 11010011,其中第一位的1是符号位,表示是负数。
-83的反码是 10101100,其中第一位的1是符号位,表示是负数。
-83的补码是 10101101,其中第一位的1是符号位,表示是负数。

有符号右移>>

正数
了解了补码之后,移位运算就简单多了,这里的右移,也是有符号右移,其移位运算就是用的补码形式。这里还是用8位的55举例。

int a = 55;
System.out.println(Integer.toBinaryString(a)); //110111
a = a >> 2;
System.out.println(Integer.toBinaryString(a)); //1101
System.out.println(a); //13

55的补码是00110111,右移之后是补码是00001101,原码和反码也是00001101即是13。
负数
负数的有符号右移,这里还用-83进行讲解。

int b = -83;
System.out.println(Integer.toBinaryString(b)); //11111111111111111111111110101101
b = b >> 2;
System.out.println(Integer.toBinaryString(b)); //11111111111111111111111111101011
System.out.println(b); // -21

-83的原码是11010011,反码是10101100,补码是10101101,所以补码右移2位后是11101011
11101011转化为反码是11101010,转化为原码是10010101,即是-21,我们可以看到,移位之后,其位数是不变的,也就是前面的符号位是不变的。

无符号右移>>>

正数
>>>是无符号右移,这里还是以55讲解

int a = 55;
System.out.println(Integer.toBinaryString(a)); //110111
a = a >>> 2;
System.out.println(Integer.toBinaryString(a)); //1101
System.out.println(a); //13

对于正数而言,有符号右移和无符号右移是一样的,因为,有符号右移,移动之后是补了符号位0,而无符号右移也是补了0,着重讲解负数。
负数
负数还是以-83来进行讲解

int b = -83;
System.out.println(Integer.toBinaryString(b)); //11111111111111111111111110101101
b = b >>> 2;
System.out.println(Integer.toBinaryString(b)); //111111111111111111111111101011
System.out.println(b); // 1073741803

看到这个结果是不是很意外,下面来进行细说一下,你就觉得很合理了。
首先-83的原码是11010011,反码是10101100,补码是10101101,这是我们上面以8位进行表示的,很正常,但是在这里就不能简单的用8位来表示了,因为其在无符号右移的时候,前面移出了0,而0取反之后变为了1,所以导致数值变得不敢相信。
这里进行32位详细模拟
-83的原码是 11111111111111111111111111010011
-83的反码(原码数值位取反)是 11111111111111111111111110101100
-83的补码(反码数值位加1)是 11111111111111111111111110101101
无符号右移2位之后补码形式是00111111111111111111111111101011,
而其反码(补码数值位减1)是01000000000000000000000000010100,
其补码(反码数值位取反)是01000000000000000000000000010011

int x = 0b01000000000000000000000000010011;
System.out.println(x); //1073741843

经过检验,果然是1073741843

有符号左移<<

对于左移的话就比较简单了,因为他不牵扯到符号的问题,符号位不变就行了,由于这个原因,所以就没有<<<符号了。
这里还是以55和-83进行讲解

int c = 55;
System.out.println(Integer.toBinaryString(c)); //110111
c = c << 2;
System.out.println(Integer.toBinaryString(c)); //110111100
System.out.println(c); //220

可以看出,左边的符号位没有动,只是在左移2位的时候后面添加了两个0而已

int d = -83;
System.out.println(Integer.toBinaryString(d)); //11111111111111111111111110101101
d = d << 2;
System.out.println(Integer.toBinaryString(d)); //11111111111111111111111010110100
System.out.println(d); // -332

也是符号位不变,在左移2位的时候,后面添加了两个0

总结有无符号

有符号:左移1位,相当于乘2,右移动一位相当于除以2
无符号:那真得好好推推了。

发布了465 篇原创文章 · 获赞 317 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/HeZhiYing_/article/details/105340290