# 计算机二进制运算与实际应用

# 正整数转二进制,负整数转二进制,小数转二进制

在说明换算之前,先介绍一下次方和负次方的概念:

^ 在这里为幂等 不是位运算符的异或

2^2 = 2×2 = 4 // 次方
2^-2 = 1÷2^2 = 1÷4 = 0.25; // 负次方

# 1,正整数转二进制

在计算机中存储字节是定长的,即我们 8、16、32 位等等,6 的二进制位为 110,但如果在 8 位计算机中是 00000110,高位补零

6/2 = 3 // 余数 0
3/2 = 1 // 余数 1
1/2 = 0 // 余数 1
// 所以值为 110

# 2,负整数转二进制

取反就是把 1 变 0,加 1 就是把最右边的 1 挪到后面一位去

// 6 在 8 位计算机中二进制是
00000110 // 先取反
11111001 // 再 + 1
11111010 // 这个值就是 - 6 的二进制

# 3,小数转二进制

小数转二进制,先把整数为转换成二进制,然后把小数位转换 (小数为换算每次乘 2,不足 1 为 0),最后相加,6.25 的二进制为 110.01

6.25 // 转二进制
6 = 110 // 整数的二进制
0.25×2 = 0.5 // 不足整数 得 0 
0.5×2 = 1 // 为整数 1
// 所以二进制为 110.01

# 二进制转换正负整数以及小数

# 1,二进制转正整数

二进制位左边首位为 0 为正数(6 —>00000110),1 为负数 (-6---->11111010)

00000110
.....2^2,2^1,2^0
2^0 × 0 = 0; // 从左往右第一个为 0
2^1 × 1 = 2; // 从左往右第二个为 1
2^2 × 1 = 4; // 从左往右第三个为 1
... // 前面都是 0,值肯定也为 0
// 所有值相加 0+2+4+0+0... = 6

# 2,二进制转负整数

-6 的二进制位为 11111010, 取反为 00000101,然后加 1 为 00000110,110 为 6,故值为 - 6

11111010 取反为00000101,然后加100000110,1106 然后再转负

# 3,二进制转小数

和小数转二进制一致,先算整数位,再算小数位,最后相加

0.01 // 二进制
2^0 2^-1 2^-2
2^0 * 0 = 0;
2^-1 * 0 = 0;
2^-2 * 1 = 1 ÷ 2^2 = 1 ÷ 4 = 0.25;
0+0+0.25 = 0.25 // 值为 0.25

# 原码,反码,补码

建议阅读该文章:原码 反码 补码 概念 原理 详解

  1. 计算机操作的都是补码
  2. 正数的原码,反码,补码一样
  3. 负数的反码,符号位不变 (第一位),其它位反过来,补码,在反码基础上加一
X=+101011 , [X]原= 0010_1011 ,[X]反=0010_1011,[X]补=0010_1011
X=-101011 , [X]原= 1010_1011 ,[X]反=1101_0100,[X]补=1101_0101

# 位运算符

# & (位与)

// 是将两边的数转换为二进制位,然后运算最终值,运算规则即 (两个为真才为真)
1&1=1 , 1&0=0 , 0&1=0 , 0&0=0
0000 0011 //3 的二进制
0000 0101 //5 的二进制
0000 0001 // 3 & 5 的值为 1
// 特性
1.一个数 & 一个数 = 最小值为0,最大值为这两个中最小数
2.一个大于1的数 & 这个数-1 = 如果等于 0 则这个数肯定是2的幂等(因为只有2的幂等数转换为2进制只有一个1)
// 1. 可用于实现奇偶数判断 因为偶数的二进制最后一位肯定是 0 , 而 1 的二进制为 00000001
// 所以任何整数 & 1 的值不是 0 就是 1
if(0 == (a & 1)) {
 // 偶数
}
// 2. 可用于数据范围落点,利用第一个特性
int[] a = new int[6];
int 51; // 要存放的数据
51 & a.lenght-1 = 1 // 得到下标
a[1] = 51; // 下次找时也可以继续通过这种方式可实现 O (1) 时间复杂度,
// HashMap 的数组就是这么做的,但数组长度尽量是 2^n 次方,因为长度 - 1 后二进制值后面都是 1, 因为与特性保证入参值的 0 或 1 都有效,减少 hash 冲突
// 3. 可以用于判断是否为 2 的幂等,利用第二个特性
a > 1 && (a & (a-1)) == 0

# | (位或)

// 是将两边的数转换为二进制位,然后运算最终值,运算规则即 (一个为真即为真)
1|0 = 1 , 1|1 = 1 , 0|1 = 1 , 0|0 = 0
0000 0011 //3 的二进制
0000 0101 //5 的二进制
0000 0111 //3 | 5 的值为 7
// 特性
1.一个数 & 一个数 = 最小值为这两个中最大数,最大值为这个两个数之和

# ^ (异或)

// 异就是不同,其运算规则为 (相同为 0, 不同为 1)
1^0 = 1 , 1^1 = 0 , 0^1 = 1 , 0^0 = 0
0000 0011 //3 的二进制
0000 0101 //5 的二进制
0000 0110 //3 ^ 5 的值为 6
// 特性
根据运算规则 1^0 = 1 , 1^1 = 0 , 0^1 = 1 可看出构成一个闭环。二进制后数据就这个样子所以可以得出结论
1.知道任意两个数可以异或出另一个数,且这三个数任意两数异或都是剩下的数
2.一个数 ^ 相同数 = 0
3.一个数 ^ 0 = 这个数
// 1. 数字换位
a ^= b; // a = a ^ b
b ^= a; // b = a ^ b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a
a ^= b; // a = a ^ b = (a ^ b) ^ b = (a ^ a) ^ b = b ^ 0 = b

# ~ (取反)

// 在二进制下,取反运算规则是
1 = 0; 0 = 1;
0000 0101; // 5 的二进制
1111 1010; //-6 的二进制
~5 = -6 // 这就是取反
// 特性
1.将二进制01弄反,不是反码首位也会改动
2.~一个数 = 这个数的反数+1
// 1. 获取绝对值 (int 最小负数值不变)
a > 0 ? a : ~a+1;

# <<(左移)

// 5<<2 的意思为 5 的二进制位往左挪两位,右边补 0, 运算规则为 (向左挪 n 位,就是原数 ×2 的 n 次幂)
5<<2
0000 0101 //5 的二进制
0001 0100 // 向左移动 2 位也就是 ×2 的 2 次幂等于十进制值 20
-5>>2
1111 1011 //-5 的二进制
11(左移丢弃) 1110 1100 // 左移 2 位前面丢掉的也就是 ×2 的 2 次幂等于十进制值 -20
// 特性
1.移动时最左边第一个数永远不变
2.移动时尾数补0
3.向左移就是×2的n次幂,移动n次就是2的n次幂

# >> (右移)

// 5>>2 的意思为 5 的二进制位往右挪两位,左边补 0 (负数左边补 1), 运算规则 (向右挪 n 位,就是原数 ÷2 的 n 次幂,余数丢弃)
5>>2
0000 0101 //5 的二进制
0000 0001 01(丢弃) // 向右移动 2 位也就是 ÷2 的 2 次幂,5÷2=2 (余数丢弃) 2÷2 = 1 (最终值)
-5>>2
1111 1011 //-5 的二进制
1111 1110 11(丢弃) // 右移动 2 位也就是 ÷2 的 2 次幂,5÷2=2 (余数 1 加上) 3÷2 = 1 (余数 1 加上) = 2 再转负数 -2 (最终值)
// 特性
1.移动时正数左边第一位补0,负数补1
2.向右移就是÷2的n次幂,移动n次就是2的n次幂
3.负数每次移动如果有余数会加上余数,最小值为-1,正数最小值为0

# >>> (无符号右移)

// 与右移的区别在一个特性是移动时正数左边第一位补 0,负数补 1 改为负数也补 0
5>>>2
1111 1011 //-5 的二进制
0011 1110 11(丢弃) // 值 62. 没想到好的算数过程

# 组合使用

# 1. 生成第一个大于 a 的满足 2^n 的数

public int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1; // >>> 获取一个向右偏差一位的二进制,再通过位或 | 的特性填充原二进制,可以得到一个不会有单个 1 出现的二进制。如 19 [00010011], 弄完后 [00011011]
    n |= n >>> 2; // >>> 获取一个向右偏差两位的二进制,再通过位或 | 的特性填充原二进制,可以得到一个不会有两个 1 出现的二进制。如 19 [00010011], 弄完后 [00011111]
    n |= n >>> 4; // ..
    n |= n >>> 8; // ..
    n |= n >>> 16;//.. 结果是从第一个 1 开始后面全面 1 的二进制。这样对这个数 + 1 可以得到一个只有一个 1 存在的二进制,那么这个二进制肯定是 2 的幂等
    return (n < 0) ? 1 : (n >= 1 << 30) ? 1 << 30 : n + 1; // 第一层判断是正负数判断,第二层判断是否大于 int 类型正整数范围内最后一位 2 幂等数,都不满足就加 +,将后面一排 1 变为最前面一个 1
}

# 2. 求绝对值 (比较是右移)

//a >> 31 int 类型 32 位,移动 31 看最前面 1 位是 1 还是 0,代表正负
a >> 31 == 0 ? a : (~a + 1);

# 3. 计算二进制中 1 的个数

count = 0  
while(a){ //a 等于 0 时退出循环
  a = a & (a - 1);  
  count++;  
}

# 4. 判断是否为 2. 的幂等

a > 1 && (a & (a - 1)) == 0

# 5. 将整数 A 转换为 B, 需要改变多少 bit 位

// 第 32 位相等
a ^= b; // 通过异或相同为 0,不相同为 1,再判断 1 的个数
count = 0  
while(a){  
  a = a & (a - 1);  
  count++;  
}
count // 次数

# 总结

待续~~