[Performance] 二进制与性能优化
二进制基础知识
1. 二进制表示数
一个格子 --> bit(位):计算机中最小的存储单位
2 个bit
种类数:22 = 4
数的范围:0~3(0~22 - 1)
8 个 bit 一组:byte:计算机中最小的访问单位
种类数:28 = 256
数的范围:0~255(0~28 - 1)
2. 反码与补码
n 个格子:
种类数:2n
数的范围:0~2n - 1
拿最左边的位作为符号位:0 就是正数,1 就是负数
1 个 byte(8位)
- 无符号位:0~28 - 1(0~255)
- 有符号位:-27 ~ 27 - 1(-128 ~ 127)
1000 000 代表的就是 -128
| 二进制 | 十进制 |
|---|---|
00000000 |
0 |
00000001 |
+1 |
| ... | ... |
01111111 |
+127 |
10000000 |
-128 ← 只有这一个 |
10000001 |
-127 |
| ... | ... |
11111111 |
-1 |
负数具体的二进制表示:反码和补码
反码:将二进制数的每一位进行反转,1 --> 0, 0-->1
补码:反码的基础上 +1
补码特点:
- 零只有一种:0000 0000,而1000 000 不是负0,而是-128
- 正数的补码和源码相同,负数补码源码取反+1
- 计算机底层都是采用补码来做计算
7
原码:0000 0111
补码:0000 0111
-3
3源码:0000 0011
-3源码:1000 0011
-3反码:1111 1100
-3补码:1111 1101
3. 位相关操作
| 操作符 | 名称 | 说明 |
|---|---|---|
& |
按位与(AND) | 两个对应位都为 1 时结果为 1 |
| ` | ` | 按位或(OR) |
^ |
按位异或(XOR) | 对应位不同时为 1,相同时为 0 |
~ |
按位取反(NOT) | 每一位取反(0→1, 1→0) |
<< |
左移(Left Shift) | 向左移动位,右边补 0 |
>> |
有符号右移(Sign-propagating Right Shift) | 向右移动位,左边补符号位 |
>>> |
无符号右移(Zero-fill Right Shift) | 向右移动位,左边补 0 |
1. 按位与
5 & 3
101
011
---
001
2. 按位或
5 | 3
101
011
---
111
3. 按位异或
不同的时候为1,相同为0
5 ^ 3
101
011
---
110
4. 按位取反
~5
101
---
010
5. 左移:左移后右边补0
5 << 1
0000 0101
---------
0000 1010
6. 右移
-
有符号:右移的时候,左边补充符位
10 >> 1 0000 1010 --------- 0000 0101-10 >> 1 1111 0110 --------- 1111 1011 -
无符号:直接左边补充0,这意味结果一定是一个非负数
10 >>> 1 0000 1010 --------- 0000 0101-10 >> 1 1111 0110 --------- 0111 1011-8 >> 2 1111 1001 --------- 0011 1110
二进制特性与性能优化
特性1:如果二进制数的第 0 位为 1,则该数字为奇数,如果第 0 位为 0,则该数为偶数
3: 0000 0011
4: 0000 0100
5: 0000 0101
6: 0000 0110
7: 0000 0111
判断一个数 num 是否为偶数
num % 2 === 0
现在:
num & 1 === 0
0000 0011
0000 0001
---------
0000 0001
0000 0100
0000 0001
---------
0000 0000
计算机底层,只有加法。
减法:7 - 4 转换 7 + (-4)
乘法:2 * 3 转换 2 + 2 + 2
除法:12 / 6 转换 12 - 6 - 6
取余:7 % 3 转换 7 - 3 - 3
特性2:如果二进制数的低 n 位都是 0,则该数可以被 2n 整除
| 十进制 | 二进制 | 低位情况 | 能否被整除 |
|---|---|---|---|
| 4 | 0000 0100 | 低 2 位为 0 | 能被 2²=4 整除 |
| 8 | 0000 1000 | 低 3 位为 0 | 能被 2³=8 整除 |
| 12 | 0000 1100 | 低 2 位为 0 | 能被 2²=4 整除 |
| 10 | 0000 1010 | 低 1 位为 0 | 能被 2¹=2 整除 |
从右往左依次代表 2⁰、2¹、2²、2³……
如果一个数的低 n 位全是 0,意味着它的二进制结构中没有包含 2⁰ 到 2ⁿ⁻¹ 这些成分,因此它一定是 2ⁿ 的倍数。
// 判断 num 是否为 2ⁿ 的倍数
function isDivisibleByPowerOfTwo(num, n) {
return (num & ((1 << n) - 1)) === 0;
}
(1 << n) 表示的是 2ⁿ,1 << 3 表示 23
0000 0001(1)
---------
0000 1000(8)
(1 << n) - 1) 生成低 n 位全为 1 的掩码
(1 << 3) - 1
0000 0001
---------
0000 1000
---------
0000 0111
掩码就是一种遮盖
1011 0110(值)
0000 1111(掩码)
0000 0110(结果)
特性3:如果二进制数除了第 n 位为 1,其它为均为 0,那么该数等于 2ⁿ
| 二进制表示 | 唯一为 1 的位 | 该位代表的值 | 十进制结果 |
|---|---|---|---|
| 0000 0001 | 第 0 位 | 2⁰ | 1 |
| 0000 0010 | 第 1 位 | 2¹ | 2 |
| 0000 0100 | 第 2 位 | 2² | 4 |
| 0000 1000 | 第 3 位 | 2³ | 8 |
| 0001 0000 | 第 4 位 | 2⁴ | 16 |
判断一个数是否为 2 的幂次方
function powerOfTwo(n) {
if (n < 1) return false;
while (n % 2 === 0) {
n = n / 2;
}
return n === 1;
}
function powerOfTwo(n) {
return n > 0 && (n & (n - 1)) === 0;
}
n - 1 会把 n 的最低位的那一个 1 变成 0,同时把其右边所有位变成 1。之后再和 n 本身做按位与运算,若结果为 0,说明 n 只有一位是 1(即 2 的幂)。
下面是一些验证示例:
| n | 二进制 | n-1 | n & (n-1) | 结果 |
|---|---|---|---|---|
| 1 | 0000 0001 | 0000 0000 | 0000 0000 | ✅ 2⁰ |
| 2 | 0000 0010 | 0000 0001 | 0000 0000 | ✅ 2¹ |
| 3 | 0000 0011 | 0000 0010 | 0000 0010 | ❌ |
| 4 | 0000 0100 | 0000 0011 | 0000 0000 | ✅ 2² |
| 6 | 0000 0110 | 0000 0101 | 0000 0100 | ❌ |
| 8 | 0000 1000 | 0000 0111 | 0000 0000 | ✅ 2³ |
特性4:将二进制数左移 n 位约等于乘以 2ⁿ,右移 n 位约等于除以 2ⁿ
去除小数
3.9 ---> 3
- 负数的结果有问题
Math.floor(-3.9)--> -4 - 效率不高
JS 中位操作的特性,JS 中的所有数字都以 64 位浮点数(双精度) 存储,但在执行位操作时,会临时转换为 32 位有符号整数,运算完成后再转回 64 位浮点数作为结果。这意味着:
- 位运算的结果始终是一个 32 位整数;
- 小数会被截断;
- 超过 32 位的部分会被丢弃。
3.9 | 0
~~3.9
交换两个数
let a = 5;
let b = 3;
let c = a;
a = b;
b = c
[a, b] = [b, a]
a = a + b;
b = a - b;
a = a - b;
a = a ^ b;
b = a ^ b;
a = a ^ b;
二进制业务应用场景
权限管理
if(value === A){
// ...
} else if (value === B){
// ...
}
该情况下就可以使用二进制。
权限关系:
| 下载 | 打印 | 查看 | 审核 | 详细 | 删除 | 编辑 | 创建 |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0:没有此权限
1:有此权限
0000 0001:只有创建权限
0010 0011:查看、编辑、创建权限
添加权限
当前:0000 0011(创建和编辑)
开放查看权限:0010 0000
当前权限 | 查看权限
0000 0011
0010 0000
---------
0010 0011
删除权限
做异或操作
当前:0010 0011
取消编辑权限:0000 0010
0010 0011
0000 0010
---------
0010 0001
判断是否拥有一个权限
做按位与操作:如果返回值是权限值本身,就代表有这个权限,如果返回的是0,代表没有这个权限
当前:0011 1100(查看、审核、详细、删除)
是否有创建权限
0011 1100
0000 0001
---------
0000 0000
是否有查看权限
0011 1100
0010 0000
---------
0010 0000

浙公网安备 33010602011771号