* 推荐阅读*
*
*
*
企业回雅克德罗,名士,尊达手表维修服务热线:400-185-6077,江苏省手表维修服务地址位于:南京市秦淮区汉中路1号新街口国际金融中心10楼H;苏州市工业园区苏州中心办公楼C座22层08室;无锡市梁溪区人民中路139号恒隆广场写字楼1座11层1104室。服务时间。
*
在平时看各种框架的源码的过程中,经常会看到一些位移运算,所以作为一个Java开发者是一定掌握位移运算的。
01 正数位移运算
Java中有三个位移运算:
① <<:左移
② >>:右移
③ >>>:无符号右移
我们直接看一下Demo:
原码反码补码计算公式及关系,System.out.println(2 << 1); // 4System.out.println(2 >> 1); // 1System.out.println(2 >>> 1); // 1System.out.println(-2 << 1); // -4System.out.println(-2 >> 1); // -1System.out.println(-2 >>> 1); // 2147483647
乍一眼看到上面Demo的打印结果,你应该是懵逼的,接下来我来解释一下这个结果到底是如何运算出来的。
2 << 1:十进制“2”转换成二进制为“00000000 00000000 00000000 00000010”,再将二进制左移一位,高位丢弃,低位补0,所以结果为“00000000 00000000 00000000 00000100”,换算成十进制则为“4”
2 >> 1:十进制“2”转换成二进制为“00000000 00000000 00000000 00000010”,再将二进制右移一位,低位丢弃,高位补0,所以结果为“00000000 00000000 00000000 00000001”,换算成十进制则为“1”
补码是这样计算的:\r\n首位为符号位。\r\n符号位为0表示正数,正数的补码=原码=反码。\r\n符号位为1表示负数,负数的补码等于将符号位后面的位全部取反再加1。另外,一个数的补码的补码等于这个数的原码,所以如果。
对于这两种情况非常好理解,那什么是无符号右移,以及负数是怎么运算的呢?
我们先来看-2 << 1与-2 >> 1,这两个负数的左移与右移操作其实和正数类似,都是先将十进制数转换成二进制数,再将二进制数进行移动,所以现在的关键是负数如何用二进制数进行表示。
02 原码、反码、补码
2.1 原码
2.2 反码
一个数字用原码表示是容易理解的,但是需要单独的一个bit来表示符号位。并且在进行加法时,计算机需要先识别某个二进制原码是正数还是负数,识别出来之后再进行相应的运算。这样效率不高,能不能让计算机在进行运算时不用去管符号位,也就是说让符号位也参与运算,这就要用到反码。
正数的反码和原码一样,负数的反码就是在原码的基础上符号位保持不变,其他位取反。
那么我们来看一下,用反码直接运算会是什么情况,我们以5-3举例。
正数的补码等于本身,负数的补码等于反码+1:例如:X=0b11(3),四比特表示原码=0011(3),对应反码为=0011(3),补码为=0011(3);X=-0b11(-3),四比特表示原码=1011(11),对应反码为=1100(12),补码为1101(
5 - 3等于5 + (-3)
这不对呀?!! 5-3=1?,为什么差了1?
我们来看一个特殊的运算:
我们来看一个特殊的运算:
我们可以看到1000 0000表示-0,0000 0000表示0,虽然-0和0是一样的,但是在用原码和反码表示时是不同的,我们可以理解为在用一个字节表示数字取值范围时,这些数字中多了一个-0,所以导致我们在用反码直接运算时符号位可以直接参加运算,但是结果会不对。
2.3 补码
为了解决反码的问题就出现了补码。
正数的补码和原码、反码一样,负数的补码就是反码+1。
5-3=2!!正确。
再来看特殊的:
1-1=0!!正确
再来看一个特殊的运算:
补码的表示方法是:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1 (即在反码的基础上+1)。因此我们在求一个数的补码之前,应当先求出这个数的原码。将一个数转化为二。
0+0=0!!也正确。
所以,我们可以看到补码解决了反码的问题。
所以对于数字,我们可以使用补码的形式来进行二进制表示。
03 负数位移运算
我们再来看-2 << 1与-2 >> 1。
-2用原码表示为10000000 00000000 00000000 00000010
-2用反码表示为11111111 11111111 11111111 11111101
-2用补码表示为11111111 11111111 11111111 11111110
-2 << 1,表示-2的补码左移一位后为11111111 11111111 11111111 11111100,该补码对应的反码为
补码的加法是怎么算的?就是按照二进制相加,逢二进一。例如:[+45]补码 = 0010 1101 [-83]补码 = 1010 1101 --按照二进制相加--- 得: 1101 1010 = [-38]补 。
该反码对应的原码为:符号位不变,其他位取反,为10000000 00000000 00000000 00000100,表示-4。
所以-2 << 1 = -4。
同理-2 >> 1是一样的计算方法,这里就不演示了。
04 无符号右移
上面在进行左移和右移时,我有一点没讲到,就是在对补码进行移动时,符号位是固定不动的,而无符号右移是指在进行移动时,符号位也会跟着一起移动。比如-2 >>> 1。
-2用原码表示为10000000 00000000 00000000 00000010
-2用反码表示为11111111 11111111 11111111 11111101
-2用补码表示为11111111 11111111 11111111 11111110
-2的补码右移1位为:01111111 11111111 11111111 11111111
右移后的补码对应的反码、原码为:01111111 11111111 11111111 11111111(因为现在的符号位为0,表示正数,正数的原、反、补码都相同)
所以,对应的十进制为2147483647。
也就是-2 >>> 1 = 2147483647
05 总结
文章写的可能比较乱,希望大家能看懂,能有所收获。这里总结一下,我们可以发现:
2 << n = 2 * (2的n次方)
m << n = m * (2的n次方)
如果觉得有所收获的话可以点赞关注我和转发分享给更多的程序猿们哦~