Java位移运算符功能详解
目录
准备工作
- 计算机基础知识:数字在内存中实际以二进制补码存在
- 位移操作符实际操作的就是补码,不是在原码上移动
- 正数的原码、反码、补码相同,负数则按下面步骤计算出补码。
- Java中数字都有符号,左边第一位就是符号位,1表示负数,0表示非负数
- 以二进制原码形式打印数字:Integer.toString(num,2),num为要打印的十进制Int数字,2表示二进制
- 以二进制补码打印数字:Integer.toBinaryString(num)
- 原码:数字的二进制表示码(十进制转二进制手动计算:把十进制数连续对2取余直到商为0,把余数倒序排列就是要求的二进制)
- 反码:最左边符号位不变,剩下所有位取反,0变1,1变0
- 补码:反码加 1
开始解析
1、<< 左移(不存在<<<)
测试代码如下:cf()方法是我自定义的将int转换成二进制补码并格式化的方法,完整原码在最后
// 左移
static void leftMove(){
System.out.println("int最小值补码 = " + cf(Integer.MIN_VALUE));
System.out.println("int最大值补码 = " + cf(Integer.MAX_VALUE));
int num = -129;
String binary = bf(num); // 原码
String complement = cf(num); // 补码
System.out.println(num + " 原码 " + binary);
System.out.println(num + " 补码 " + complement);
System.out.println("---------------------------------------------");
System.out.println(complement + " << 0 补码 = " + cf(num<<0) +" ,十进制 "+ (num<<0));
System.out.println(complement + " << 32 补码 = " + cf(num<<32) +" ,十进制 "+ (num<<32));
System.out.println(complement + " << 4 补码 = " + cf(num<<4) +" ,十进制 "+ (num<<4));
System.out.println(complement + " << 36 补码 = " + cf(num<<36) +" ,十进制 "+ (num<<36));
System.out.println(complement + " << 24 补码 = " + cf(num<<24) +" ,十进制 "+ (num<<24));
System.out.println(complement + " << 31 补码 = " + cf(num<<31) +" ,十进制 "+ (num<<31));
}打印结果如下:
int最小值补码 = 1000_0000_0000_0000_0000_0000_0000_0000
int最大值补码 = 0111_1111_1111_1111_1111_1111_1111_1111
-129 原码 1000_0000_0000_0000_0000_0000_1000_0001
-129 补码 1111_1111_1111_1111_1111_1111_0111_1111
---------------------------------------------
1111_1111_1111_1111_1111_1111_0111_1111 << 0 补码 = 1111_1111_1111_1111_1111_1111_0111_1111 ,十进制 -129
1111_1111_1111_1111_1111_1111_0111_1111 << 32 补码 = 1111_1111_1111_1111_1111_1111_0111_1111 ,十进制 -129
1111_1111_1111_1111_1111_1111_0111_1111 << 4 补码 = 1111_1111_1111_1111_1111_0111_1111_0000 ,十进制 -2064
1111_1111_1111_1111_1111_1111_0111_1111 << 36 补码 = 1111_1111_1111_1111_1111_0111_1111_0000 ,十进制 -2064
1111_1111_1111_1111_1111_1111_0111_1111 << 24 补码 = 0111_1111_0000_0000_0000_0000_0000_0000 十2130706432
1111_1111_1111_1111_1111_1111_0111_1111 << 31 补码 = 1000_0000_0000_0000_0000_0000_0000_0000 十-2147483648
三种颜色数字含义:
- 蓝色:符号位,正数0,负数1
- 红色:初始有效位数。-129二进制值为 1000_0001,其他黑色数字都是补码,属于自动填充占位的
- 绿色:左移,右边空位补0,绿色0的个数等于左移位数
小结:
- num << n ,先要进行n%32(num为int),或者n%64(num为long),-129 << 0 == -129 << 32,-129 << 4 == -129 << 36
- 符号位的值要参与移动,且第一个被挤掉,最终的符号要看移动结束停在第一位的值是0还是1,-129 << 24 == 0111_1111_0000_0000_0000_0000_0000_0000
- 最小值是特殊值,1000_0000_0000_0000_0000_0000_0000_0000,使用Integer.toBinaryString(Integer.MIN_VALUE) 得到 -1000_0000_0000_0000_0000_0000_0000_0000
算上前面的负号,共有33位。且最小值在Int类型中只能用补码不能转换出原码和反码,直接就是 -2^31。类似的在Long类型中的最小值也是特殊值。
2、>> 高位以符号位填充
/*-------------右移--------------*/
static void rightMove(){
int num = -129;
String complement = cf(num); // 补码
System.out.println(complement + " >> 0 补码 = " + cf(num>>0) +" ,十进制 "+ (num>>0));
System.out.println(complement + " >> 4 补码 = " + cf(num>>4) +" ,十进制 "+ (num>>4));
System.out.println(complement + " >> 36 补码 = " + cf(num>>36) +" ,十进制 "+ (num>>36));
System.out.println(complement + " >> 8 补码 = " + cf(num>>8) +" ,十进制 "+ (num>>8));
System.out.println(complement + " >> 24 补码 = " + cf(num>>24) +" ,十进制 "+ (num>>24));
}打印结果:
1111_1111_1111_1111_1111_1111_0111_1111 >> 0 补码 = 1111_1111_1111_1111_1111_1111_0111_1111 ,十进制 -129
1111_1111_1111_1111_1111_1111_0111_1111 >> 4 补码 = 1111_1111_1111_1111_1111_1111_1111_0111 ,十进制 -9
1111_1111_1111_1111_1111_1111_0111_1111 >> 36 补码 = 1111_1111_1111_1111_1111_1111_1111_0111 ,十进制 -9
1111_1111_1111_1111_1111_1111_0111_1111 >> 8 补码 = 1111_1111_1111_1111_1111_1111_1111_1111 ,十进制 -1
1111_1111_1111_1111_1111_1111_0111_1111 >> 24 补码 = 1111_1111_1111_1111_1111_1111_1111_1111 ,十进制 -1
小结:
- 符号位会参与 >>,看标记蓝色的1
- >> 高位以符号位填充,正数填0,负数填1
- 这里做了负数,后面会做一个正数负数对比
- num >> n,先n%32或者n%64(num为long),看4和36的结果一样
- n >= num的有效位数,结果全是 -1
3、>>> 高位以0填充
static void unsignedRightMove(){
int num = -129;
String complement = cf(num); // 补码
System.out.println(complement + " >>> 0 补码 = " + cf(num>>>0) +" ,十进制 "+ (num>>>0));
System.out.println(complement + " >>> 4 补码 = " + cf(num>>>4) +" ,十进制 "+ (num>>>4));
System.out.println(complement + " >>> 36 补码 = " + cf(num>>>36) +" ,十进制 "+ (num>>>36));
System.out.println(complement + " >>> 8 补码 = " + cf(num>>>8) +" ,十进制 "+ (num>>>8));
System.out.println(complement + " >>> 24 补码 = " + cf(num>>>24) +" ,十进制 "+ (num>>>24));
System.out.println(complement + " >>> 31 补码 = " + cf(num>>>31) +" ,十进制 "+ (num>>>31));
System.out.println(complement + " >>> 32 补码 = " + cf(num>>>32) +" ,十进制 "+ (num>>>32));
}打印结果:
1111_1111_1111_1111_1111_1111_0111_1111 >>> 0 补码 = 1111_1111_1111_1111_1111_1111_0111_1111 ,十进制 -129
1111_1111_1111_1111_1111_1111_0111_1111 >>> 4 补码 =0000_1111_1111_1111_1111_1111_1111_0111 ,十进制 268435447
1111_1111_1111_1111_1111_1111_0111_1111 >>> 36 补码 = 0000_1111_1111_1111_1111_1111_1111_0111 ,十进制 268435447
1111_1111_1111_1111_1111_1111_0111_1111 >>> 8 补码 = 0000_0000_1111_1111_1111_1111_1111_1111 ,十进制 16777215
1111_1111_1111_1111_1111_1111_0111_1111 >>> 24 补码 = 0000_0000_0000_0000_0000_0000_1111_1111 ,十进制 255
1111_1111_1111_1111_1111_1111_0111_1111 >>> 31 补码 = 0000_0000_0000_0000_0000_0000_0000_0001 ,十进制 1
1111_1111_1111_1111_1111_1111_0111_1111 >>> 32 补码 = 1111_1111_1111_1111_1111_1111_0111_1111 ,十进制 -129
小结:
- >>> 又叫无符号右移。左边高位补零,看绿色标记
- >>> 有效位移数为[1~31](num为int类型),移32等于移动0位。
- 只要进行了有效位移,结果都为正数,最小值为1,没有0或者负数
混合测试
/*-------------混合测试--------------*/
static void mixTest(){
int num = -129;
int num2 = 128;
String complement = cf(num); // 补码
String complement2 = cf(num2); // 补码
System.out.println(complement + " >> 4 补码 = " + cf(num>>4) +" ,十进制 "+ (num>>4));
System.out.println(complement + " >>> 4 补码 = " + cf(num>>>4) +" ,十进制 "+ (num>>>4));
System.out.println(complement + " >> 8 补码 = " + cf(num>>8) +" ,十进制 "+ (num>>8));
System.out.println(complement + " >>> 8 补码 = " + cf(num>>>8) +" ,十进制 "+ (num>>>8));
System.out.println(complement2 + " >> 4 补码 = " + cf(num2>>4) +" ,十进制 "+ (num2>>4));
System.out.println(complement2 + " >>> 4 补码 = " + cf(num2>>>4) +" ,十进制 "+ (num2>>>4));
System.out.println(complement2 + " >> 8 补码 = " + cf(num2>>8) +" ,十进制 "+ (num2>>8));
System.out.println(complement2 + " >>> 8 补码 = " + cf(num2>>>8) +" ,十进制 "+ (num2>>>8));
}打印结果:
1111_1111_1111_1111_1111_1111_0111_1111 >> 4 补码 = 1111_1111_1111_1111_1111_1111_1111_0111 ,十进制 -9
1111_1111_1111_1111_1111_1111_0111_1111 >>> 4 补码 = 0000_1111_1111_1111_1111_1111_1111_0111 ,十进制 268435447
1111_1111_1111_1111_1111_1111_0111_1111 >> 8 补码 = 1111_1111_1111_1111_1111_1111_1111_1111 ,十进制 -1
1111_1111_1111_1111_1111_1111_0111_1111 >>> 8 补码 = 0000_0000_1111_1111_1111_1111_1111_1111 ,十进制 16777215
0000_0000_0000_0000_0000_0000_1000_0000 >> 4 补码 = 0000_0000_0000_0000_0000_0000_0000_1000 ,十进制 8
0000_0000_0000_0000_0000_0000_1000_0000 >>> 4 补码 = 0000_0000_0000_0000_0000_0000_0000_1000 ,十进制 8
0000_0000_0000_0000_0000_0000_1000_0000 >> 8 补码 = 0000_0000_0000_0000_0000_0000_0000_0000 ,十进制 0
0000_0000_0000_0000_0000_0000_1000_0000 >>> 8 补码 = 0000_0000_0000_0000_0000_0000_0000_0000 ,十进制 0
总结:
- 对于负数,带符号右移和无符号右移结果差别很大
- 对于正数,带符号右移和无符号右移结果一样
完整测试代码:
package com.qing.advance;
public class Test07 {
/*-------------左移--------------*/
static void leftMove(){
System.out.println("int最小值补码 = " + cf(Integer.MIN_VALUE));
System.out.println("int最大值补码 = " + cf(Integer.MAX_VALUE));
int num = -129;
String binary = bf(num); // 原码
String complement = cf(num); // 补码
System.out.println(num + " 原码 " + binary);
System.out.println(num + " 补码 " + complement);
System.out.println("---------------------------------------------");
System.out.println(complement + " << 0 补码 = " + cf(num<<0) +" ,十进制 "+ (num<<0));
System.out.println(complement + " << 32 补码 = " + cf(num<<32) +" ,十进制 "+ (num<<32));
System.out.println(complement + " << 4 补码 = " + cf(num<<4) +" ,十进制 "+ (num<<4));
System.out.println(complement + " << 36 补码 = " + cf(num<<36) +" ,十进制 "+ (num<<36));
System.out.println(complement + " << 24 补码 = " + cf(num<<24) +" ,十进制 "+ (num<<24));
System.out.println(complement + " << 31 补码 = " + cf(num<<31) +" ,十进制 "+ (num<<31));
}
/*-------------右移--------------*/
static void rightMove(){
int num = -129;
String complement = cf(num); // 补码
System.out.println(complement + " >> 0 补码 = " + cf(num>>0) +" ,十进制 "+ (num>>0));
System.out.println(complement + " >> 4 补码 = " + cf(num>>4) +" ,十进制 "+ (num>>4));
System.out.println(complement + " >> 36 补码 = " + cf(num>>36) +" ,十进制 "+ (num>>36));
System.out.println(complement + " >> 8 补码 = " + cf(num>>8) +" ,十进制 "+ (num>>8));
System.out.println(complement + " >> 24 补码 = " + cf(num>>24) +" ,十进制 "+ (num>>24));
}
/*-------------无符号右移--------------*/
static void unsignedRightMove(){
int num = -129;
String complement = cf(num); // 补码
System.out.println(complement + " >>> 0 补码 = " + cf(num>>>0) +" ,十进制 "+ (num>>>0));
System.out.println(complement + " >>> 4 补码 = " + cf(num>>>4) +" ,十进制 "+ (num>>>4));
System.out.println(complement + " >>> 36 补码 = " + cf(num>>>36) +" ,十进制 "+ (num>>>36));
System.out.println(complement + " >>> 8 补码 = " + cf(num>>>8) +" ,十进制 "+ (num>>>8));
System.out.println(complement + " >>> 24 补码 = " + cf(num>>>24) +" ,十进制 "+ (num>>>24));
System.out.println(complement + " >>> 31 补码 = " + cf(num>>>31) +" ,十进制 "+ (num>>>31));
System.out.println(complement + " >>> 32 补码 = " + cf(num>>>32) +" ,十进制 "+ (num>>>32));
}
/*-------------混合测试--------------*/
static void mixTest(){
int num = -129;
int num2 = 128;
String complement = cf(num); // 补码
String complement2 = cf(num2); // 补码
System.out.println(complement + " >> 4 补码 = " + cf(num>>4) +" ,十进制 "+ (num>>4));
System.out.println(complement + " >>> 4 补码 = " + cf(num>>>4) +" ,十进制 "+ (num>>>4));
System.out.println(complement + " >> 8 补码 = " + cf(num>>8) +" ,十进制 "+ (num>>8));
System.out.println(complement + " >>> 8 补码 = " + cf(num>>>8) +" ,十进制 "+ (num>>>8));
System.out.println(complement2 + " >> 4 补码 = " + cf(num2>>4) +" ,十进制 "+ (num2>>4));
System.out.println(complement2 + " >>> 4 补码 = " + cf(num2>>>4) +" ,十进制 "+ (num2>>>4));
System.out.println(complement2 + " >> 8 补码 = " + cf(num2>>8) +" ,十进制 "+ (num2>>8));
System.out.println(complement2 + " >>> 8 补码 = " + cf(num2>>>8) +" ,十进制 "+ (num2>>>8));
}
public static void main(String[] args) {
// leftMove();
// rightMove()
// unsignedRightMove();
mixTest();
}
/*-------------将二进制补码格式化--------------*/
static String cf(Integer num){
return intFormat(Integer.toBinaryString(num));
}
/*-------------将二进制原码格式化--------------*/
static String bf(Integer num){
return intFormat(Integer.toString(num,2));
}
/*-------------补零、加下划线--------------*/
static String intFormat(String str){
StringBuilder sb = new StringBuilder(str);
// 极小值 -100_0000_0000_0000_0000_0000_0000_00000 加上负号会有33位,其余都是32位
if(sb.length()==33){
sb.deleteCharAt(0);
}else if(sb.charAt(0) == '-'){
sb.setCharAt(0,'1');
// 负数在符号位后面补零
while (sb.length() < 32){
sb.insert(1,'0');
}
}
// 正数在最前面补零
while (sb.length() < 32){
sb.insert(0,'0');
}
for (int i=28;i>0;i-=4){
sb.insert(i,'_');
}
return sb.toString();
}
}