Java位移运算符<<和 >>和 >>>原理详解

Java位移运算符功能详解

目录

Java位移运算符功能详解

准备工作

开始解析

1、<< 左移(不存在<<<)

三种颜色数字含义:

小结:

2、>> 高位以符号位填充

小结:

3、>>> 高位以0填充

小结:

混合测试

总结:

完整测试代码:


准备工作

  1. 计算机基础知识:数字在内存中实际以二进制补码存在
  2. 位移操作符实际操作的就是补码,不是在原码上移动
  3. 正数的原码、反码、补码相同,负数则按下面步骤计算出补码。
  4. Java中数字都有符号,左边第一位就是符号位,1表示负数,0表示非负数
  5. 以二进制原码形式打印数字:Integer.toString(num,2),num为要打印的十进制Int数字,2表示二进制
  6. 以二进制补码打印数字: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的个数等于左移位数

小结:

  1. num << n ,先要进行n%32(num为int),或者n%64(num为long),-129 << 0 == -129 << 32,-129 << 4 == -129 << 36
  2. 符号位的值要参与移动,且第一个被挤掉,最终的符号要看移动结束停在第一位的值是0还是1,-129 << 24 == 0111_1111_0000_0000_0000_0000_0000_0000 
  3. 最小值是特殊值,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. 符号位会参与 >>,看标记蓝色的1
  2. >> 高位以符号位填充,正数填0,负数填1
  3. 这里做了负数,后面会做一个正数负数对比
  4. num >> n,先n%32或者n%64(num为long),看4和36的结果一样
  5. 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. >>> 又叫无符号右移。左边高位补零,看绿色标记
  2. >>> 有效位移数为[1~31](num为int类型),移32等于移动0位。
  3. 只要进行了有效位移,结果都为正数,最小值为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

总结:

  1. 对于负数,带符号右移和无符号右移结果差别很大
  2. 对于正数,带符号右移和无符号右移结果一样

 

完整测试代码:

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();
    }
}

 

 

 

 

 


版权声明:本文为qq_27545063原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。