##一、简介
Java中有三种移位运算符:
运算符 | 解释 |
---|---|
<< | 左移运算符,移动一位乘以一个2 |
>> | 右移运算符,移动一位除以一个2 |
>>> | 无符号右移,忽略符号位,空位用0补齐 |
##二、关于原码、反码、补码
Java里的byte占一个字节,一个字节8个位,8个位里的第一位是符号位,第一个符号位表示正负数。
Java里的int占四个字节,共32位,32个位里的第一位是符号位没,表示正负数。
int data = 20;
//前面省略了27个0,高位若无1,即可把0省略缩写
[+20] = 原码[10100]=反码[10100]=补码[10100]
[-20 ] = 原码[10000000000000000000000000010100] = 反码[11111111111111111111111111101011] = 补码[11111111111111111111111111101100]
1)原码就是在原数值前面加一符号位, 即用第一位表示符号, 其余位表示值。
2)反码的表示方法是:正数的反码是其本身;负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
3)补码的表示方法是:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。 (即在反码的基础上+1)
##三、位运算符
与(&)、非(~)、或(|)、异或(^)
与运算:两个操作数中位都为1,结果才为1,否则结果为0
或运算:两个位只要有一个为1,那么结果就是1,否则就为0
非运算:如果位为0,结果是1,如果位为1,结果是0
异或运算:两个操作数的位中,相同则结果为0,不同则结果为1
##四、实践出真知
package com.person.dataType;
public class ShiftOp {
public static <T> void print(T data){
System.out.println(data);
}
/**
* 左移运算:计算机存储的是补码,虽然正数的原反补都一样,我们在看待问题时务必清楚,移位运算移动的是补码,虽然原反补都一样。
*/
public static void leftShift(){
print("-------------左移运算符正数示例---------------");
//正数
int pdata = 20;
print(pdata << 1); //左移一位乘以2,Result = 40
print(Integer.toBinaryString(pdata));//二进制数据,Result = 10100,1*2^4+1*2^2=20
print(Integer.valueOf("101000", 2)); //10100的十进制为20,当使用 << 左移一位的时候,10100整个字符串向左移动一位为10100 0,低位补0
print("-------------左移运算符负数示例---------------");
//负数
int mdata = -20;
print(mdata << 1); //左移一位乘以2,Result = -40
print(Integer.toBinaryString(mdata));//负数的二进制是通过补码的形式来保存的:11111111111111111111111111101100
print(Integer.parseInt("-10100", 2));//Java本身不提供方法将二进制负数转正数,只能通过在负数的原码上将符号位1变成"-"再通过parseInt或valueOf进行转换,或者自己写一个函数?
}
/**
* 负数反码移位保持符号不变,高位始终补1
*/
public static void rightShift(){
print("-------------右移运算符正数示例---------------");
//正数
int pdata = 20;
print(pdata >> 1); //右移一位除以2,Result = 10
print(Integer.toBinaryString(pdata));//二进制数据,Result = 10100,1*2^4+1*2^2=20
print(Integer.valueOf("01010", 2)); //10100的十进制为20,当使用 >> 右移一位的时候,10100整个字符串向右移动一位为01010
print("-------------右移运算符负数示例---------------");
//负数
int mdata = -20;
print(mdata >> 1); //右移一位除以2,Result = -10
print(Integer.toBinaryString(mdata));//负数的二进制是通过补码的形式来保存的:11111111111111111111111111101100
print(Integer.parseInt("-01010", 2));//将补码装换为原码后再进行输出操作,结果为-10
}
/**
* 首先我们先要理解一个问题,计算机存储数据用的是补码,正数的原反补都一样,有符号指的是算术上的,无符号指的是自然上的,自然数哪来的负数?
* 首先储存的为补码,当进行无符号位移的时候,补码=反码=原码,和正数一样。无符号移位时,原来负数的补码移位后就变成正数的补码,有符号位移时负数的补码移
* 位后还是负数的补码,这是一个从算术上往自然上的转换,因此我们会看到这样一个现象就是将比较大的负数无符号移位后得到的正数也比较大。
*/
public static void unsignedShift(){
print("-------------无符号右移运算符正数示例---------------");
print("正数的无符号右移和正数的有符号右移一样,在高位补0");
print("-------------无符号右移运算符负数示例---------------");
print("在最高位补0,因为无符号,负数的最高位1不被当做符号");
print(-20 >>> 4);
print(Integer.parseInt("00001111111111111111111111111110", 2));//无符号补码右移高位始终补0
}
/**
* 比较两个数的符号是否相同
*/
public static boolean CompSignForEq(int data1, int data2){
return ((data1 >> 31) ^ (data2 >> 31)) == 0; //相同为0,不同为1
}
public static void main(String[] args) {
leftShift();//左移运算符使用实例
rightShift();//右移运算符使用实例
unsignedShift();//无符号移位运算符
//异或运算应用实例
print(CompSignForEq(-2, 2));//不同为false
print(Math.pow(2, 31)-1);//结果为2.147483647E9,e9的意思为10的9次方
print(Integer.MAX_VALUE);
print(Integer.MIN_VALUE);
}
}
显示结果:
-------------左移运算符正数示例---------------
40
10100
40
-------------左移运算符负数示例---------------
-40
11111111111111111111111111101100
-20
-------------右移运算符正数示例---------------
10
10100
10
-------------右移运算符负数示例---------------
-10
11111111111111111111111111101100
-10
-------------无符号右移运算符正数示例---------------
正数的无符号右移和正数的有符号右移一样,在高位补0
-------------无符号右移运算符负数示例---------------
在最高位补0,因为无符号,负数的最高位1不被当做符号
268435454
268435454
false
2.147483647E9
2147483647
-2147483648
如有问题欢迎留言区共同进步