位运算到底有什么用途呢?
可以优化代码,比一般的运算要快。
左移 << ,左移n位,就相当于乘以2的n次方
& 按位与,可以将结果范围限制在 右边数据的范围内(例:n&5,无论n为多少,结果都在0~5范围内)
1. 原码、反码、补码
- 原码
十进制数字的二进制表现形式就是原码
例如: 20 -> 0 0 0 1 0 1 0 0
左边第一位为符号位(0为正,1为负),其他位为数据位。
一个 byte 有 8bit,最大值是 0 1 1 1 1 1 1 1 (+127),最小值是 1 1 1 1 1 1 1 1 (-127)
- 反码
正数的反码是它本身,负数的反码除了符号位不变,其余数据位取反
十进制数字 | 原码 | 反码 |
+0 | 00000000 | 00000000 |
-1 | 10000001 | 11111110 |
-2 | 10000010 | 11111101 |
- 补码
正数的补码是它本身,负数的补码等于其反码+1,
十进制数字 | 原码 | 反码 | 补码 |
+0 | 00000000 | 00000000 | 00000000 |
-1 | 10000001 | 11111110 | 11111111 |
-2 | 10000010 | 11111101 | 11111110 |
2. 位运算
在java当中拿int类型来说,占4个字节,32位,也就是 0000 0000 0000 0000 0000 0000 0000 0000。
位运算都是通过补码来运算,得出的结果也是补码,所以最后还需还原成原码
运算符 | 含义 | 运算规则 |
& | 逻辑与 | 两个都为1时,结果才为1。 |
| | 逻辑或 | 两个当中只要有一个为1,结果就为1 |
~ | 按位取反 | 0变1,1变0 |
^ | 按位异或 | 相同为0,相异为1 |
<< | 左移 | 向左移动,低位补零 |
>> | 右移 | 向右移动,高位补零,符号位按照原来数字的符号位不变 |
>>> | 无符号右移 | 向右移动,高位补零 |
- 运算符 &
都为正数的情况下,在java中,如果左边的数比右边小,那么结果就是左边的数;左边的数比右边大,那么结果就是左边数除以右边数求余再减一。
5 & 3
/**
* 5 & 3
* 5 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 3 补码:0000 0000 0000 0000 0000 0000 0000 0011
* 比较值:0000 0000 0000 0000 0000 0000 0000 0001
* 打印-结果: 1
* 计算规则:第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0
**/
- 运算符 |
/**
* 5 | 3
* 5 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 3 补码:0000 0000 0000 0000 0000 0000 0000 0011
* 结果:0000 0000 0000 0000 0000 0000 0000 0111
* 打印-结果:7
* 计算规则:第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0
**/
- 运算符 ~
println("~5",~5);
/**
* 5 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 取非:1111 1111 1111 1111 1111 1111 1111 1010 (即补码)
* 反码:1111 1111 1111 1111 1111 1111 1111 1001
* 原码:1000 0000 0000 0000 0000 0000 0000 0110
* 打印-结果:-6
* 计算规则:操作数的第n位为1,那么结果的第n位为0,反之。
**/
- 运算符^
println("5 ^ 3",5 ^ 3);
/**
* 5 | 3
* 5 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 3 补码:0000 0000 0000 0000 0000 0000 0000 0011
* 结果:0000 0000 0000 0000 0000 0000 0000 0110
* 打印-结果:6
* 计算规则:第一个操作数的的第n位于第二个操作数的第n位 相反,那么结果的第n为也为1,否则为0
**/
- 左移 <<
若<<右边数字为3,就是在补码后面添加3个0,在补码最左边三位数去掉
println("5<<2", 5<<2);
/**
* 5<<2
* 原码:0000 0000 0000 0000 0000 0000 0000 0101
* 反码:0000 0000 0000 0000 0000 0000 0000 0101
* 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 左移:0000 0000 0000 0000 0000 0000 0001 0100
* 补码:0000 0000 0000 0000 0000 0000 0001 0100
* 反码:0000 0000 0000 0000 0000 0000 0001 0100
* 原码:0000 0000 0000 0000 0000 0000 0001 0100
* 打印-结果:20
* 计算规则:1、向左移动指定位,左边二进制位丢弃,右边补0。(注意byte和short类型移位运算时会变成int型,结果要强制转换)
* 2、若1被移位到最左侧,则变成负数。
* 3、左移时舍弃位不包含1,则左移一次,相当于乘2。
**/
- 右移 >>
println("5>>2", 5>>2);
/**
* 5>>2
* 原码:0000 0000 0000 0000 0000 0000 0000 0101
* 补码:0000 0000 0000 0000 0000 0000 0000 0101
* 反码:0000 0000 0000 0000 0000 0000 0000 0101
* 右移:0000 0000 0000 0000 0000 0000 0000 0001
* 补码:0000 0000 0000 0000 0000 0000 0000 0001
* 反码:0000 0000 0000 0000 0000 0000 0000 0001
* 原码:0000 0000 0000 0000 0000 0000 0000 0001
* 打印-结果:1
* 计算规则:1、数值value向右移动num位,正数左补0,负数左补1,右边舍弃。(即保留符号位)
* 2、右移一次,相当于除以2,并舍弃余数。
**/
- 无符号右移
就是符号位不进行移动
println("-5>>>3",-5>>>3);
/**
* -5>>>3
* 原码:1000 0000 0000 0000 0000 0000 0000 0101
* 反码:1111 1111 1111 1111 1111 1111 1111 1010
* 补码:1111 1111 1111 1111 1111 1111 1111 1011
* 右移:0001 1111 1111 1111 1111 1111 1111 1111 (即补码)
* 补码:0001 1111 1111 1111 1111 1111 1111 1111
* 反码:0001 1111 1111 1111 1111 1111 1111 1111
* 原码:0001 1111 1111 1111 1111 1111 1111 1111
* 打印-结果:536870911
* 计算规则:1、数值value向右移动num位,正数左补0,负数左补1,右边舍弃。(即保留符号位)
* 2、右移一次,相当于除以2,并舍弃余数。
* 3、无符号右移>>>:左边位用0补充,右边丢弃。
* 所得结果永远正数
**/