目录标题
[Q&A] 位运算支持的类型
位运算符中 ,操作数只能为整型
和字符型
数据。
[Q&A] 位运算符号
按位与(&)
同1则1 → 类比Java的&&,都为true时才为true
操作数1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作数2 | 0 | 1 | 0 | 1 |
按位与 | 0 | 0 | 0 | 1 |
按位或(|)
有1则1 → 类比Java的||,有一个为true则为true
操作数1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作数2 | 0 | 1 | 0 | 1 |
按位或 | 0 | 1 | 1 | 1 |
按位取反(~)
取反 → 类比Java的!,逻辑取反
操作数 | 0 | 1 |
---|---|---|
按位非 | 1 | 0 |
按位异或(^)
异为1同为0 → 类比两条数据使用比较工具进行比较,不同数据会被高亮
异或,英文为exclusive OR
,缩写成xor
,也叫半加运算
:异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0,这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。
操作数1 | 0 | 0 | 1 | 1 |
---|---|---|---|---|
操作数2 | 0 | 1 | 0 | 1 |
按位异或 | 0 | 1 | 1 | 0 |
左位移(<<)
溢出截断,低位补0
右位移(>>)
溢出截断,高位补符号位
无符号右移(>>>)
溢出截断,高位补0
总结
按位取反的理解
2进制 -1补码:11111111111111111111111111111111
2进制 14原码:00000000000000000000000000001110
2进制 14反码:00000000000000000000000000001110
2进制 14补码:00000000000000000000000000001110
2进制 -14原码:10000000000000000000000000001110
2进制 -14反码:11111111111111111111111111110001
2进制 -14补码:11111111111111111111111111110010
✅如果 x + y = 11111111
,可得 x 和 y 互为按位取反
的关系,不分正负时11111111是2^N-1
。如分正负时,11111111是-1
。
✅负数绝对值 和 负数反码 互为按位取反的关系 等价 负数绝对值 + 负数反码 = Mod(2^N) -1
✅负数绝对值 + 负数补码 = Mod(2^N) 推导出 负数补码 = 负数反码 + 1
原码,反码,补码之间的关系?
✅二进制里 减一取反
和 取反加一
等价
二进制里为什么减一取反和取反加一等价?
取反的 3 种方式
2进制 14补码:00000000000000000000000000001110
2进制 ~14 ☞ 11111111111111111111111111110001
2进制 14^-1 ☞ 11111111111111111111111111110001
2进制 -14-1 ☞ 11111111111111111111111111110001
2进制 -14补码:11111111111111111111111111110010
2进制 ~-14 ☞ 00000000000000000000000000001101
2进制 -14^-1 ☞ 00000000000000000000000000001101
2进制 --14-1 ☞ 00000000000000000000000000001101
private static void test(int num) {
System.out.println("10进制 " + proFillSpace("输入数:") + num);
System.out.println(" 2进制 " + proFillSpace("-1补码:") + Integer.toBinaryString(-1));
System.out.println(" 2进制 " + proFillSpace(num + "补码:") + proFillZero(Integer.toBinaryString(num)));
System.out.println(" 2进制 " + proFillSpace(-num + "补码:") + proFillZero(Integer.toBinaryString(-num)));
System.out.println(" 2进制 " + proFillSpace("~(" + num +")" + " ☞") + proFillSpace(num) + "按位取反: " + proFillZero(Integer.toBinaryString(~num)));
System.out.println(" 2进制 " + proFillSpace("(" + num + ")" +"^-1 ☞") + proFillSpace(num) + "按位取反: " + proFillZero(Integer.toBinaryString(num ^ -1)));
System.out.println(" 2进制 " + proFillSpace("-1-(" + num + ")" + " ☞") + proFillSpace(num) + "按位取反: " + proFillZero(Integer.toBinaryString(-1 - num)));
}
private static String proFillZero(String str) {
if (str.length() >= 32) {
return str;
}
return proFillZero("0" + str);
}
private static String proFillSpace(Object i) {
String str = String.valueOf(i);
return proFillSpace2(str);
}
private static String proFillSpace2(String str) {
if (str.length() >= 10) {
return str;
}
return proFillSpace2(" " + str);
}
10进制 输入数:14
2进制 -1补码:11111111111111111111111111111111
2进制 14补码:00000000000000000000000000001110
2进制 -14补码:11111111111111111111111111110010
2进制 ~(14) ☞ 14按位取反: 11111111111111111111111111110001
2进制 (14)^-1 ☞ 14按位取反: 11111111111111111111111111110001
2进制 -1-(14) ☞ 14按位取反: 11111111111111111111111111110001
与运算(&)的用途
或运算(|)的用途
异或运算(^)的用途
左位移运算(<<)的用途
右位移运算(>>)的用途
运算符组合运算场景
取绝对值
(a^(a>>31))-(a>>31) 取反加1
(a+(a>>31))^(a>>31) 减1取反
# 正数
System.out.println((0 ^ (0 >> 31)) - (0 >> 31));// 0
System.out.println((5 ^ (5 >> 31)) - (5 >> 31));// 5
System.out.println((6 ^ (6 >> 31)) - (6 >> 31));// 6
System.out.println((7 ^ (7 >> 31)) - (7 >> 31));// 7
# 负数
System.out.println((-1 ^ (-1 >> 31)) - (-1 >> 31));// 1
System.out.println((-5 ^ (-5 >> 31)) - (-5 >> 31));// 5
System.out.println((-6 ^ (-6 >> 31)) - (-6 >> 31));// 6
System.out.println((-7 ^ (-7 >> 31)) - (-7 >> 31));// 7
4字节时,右移31操作可以取得任何整数的符号位
任何正数右移31后只剩符号位0,,溢出的31位截断,空出的31位补0,最终结果为0
任何负数右移31后也只剩符号位1,溢出的31位截断,空出的31位补符号位1,最终结果为-1
int[] arr = {-3, -2, -1, 0, 1, 2, 3};
for (int i : arr) {
abs1(i);
}
// 3 2 1 0 1 2 3
for (int i : arr) {
abs2(i);
}
// 3 2 1 0 1 2 3
private static void abs1(int num) {
int i = (num ^ (num >> 31)) - (num >> 31);
System.out.print(i + " ");
}
private static void abs2(int num) {
int i = (num + (num >> 31)) ^ (num >> 31);
System.out.print(i + " ");
}
找出没有重复的数 (使用起来有太多局限,非通用)
public static int find(int[] nums){
int tmp = nums[0];
for(int i = 1;i < nums.length; i++)
tmp ^= arr[i];
return tmp;
}
参考:
几种常见的位运算
C语言位运算的妙用你知道多少?
位运算的妙用
位运算用法
位运算详解
https://imageslr.com/2019/12/11/data-lab-2.html