共计 2431 个字符,预计需要花费 7 分钟才能阅读完成。
位运算在算法中很有用,速度可以比四则运算快很多。
To2orTo10
JS 中十进制转二进制: (val).toString(2)JS 中二进制转十进制: parseInt(val, 2)
JS 中规定安全整数的范围是 -2^53~2^53,所以大于 9007199254740991 的数进制转换会存在精度问题
读取的十进制是根据原码来读取,而在内存中,数值都是以二进制补码形式保存的
十进制 - 5 的二进制表示为:1000,0101(原码)原码除符号位外,全部取反再 +1:11111011(补码,内存存储形式)
~- 5 的结果:
正数的补码和原码一样
负数原码补码转换规则: 符号位不动,从低位往高位数,遇到第一个 1 之前,包括第一个 1 不作任何取反,之后,每位都取反。避免了原码转反码再转补码的繁琐。
{原码符号位不变} + {数值位按位取反后 +1}
{原码符号位不变} + {数值位从右边数第一个 1 及其右边的 0 保持不变, 左边安位取反}
// 7
00000111 // 原码
00000111 // 补码
// -6
10000110 // 原码
11111010 // 补码 // {原码符号位不变} + {数值位从右边数第一个 1 及其右边的 0 保持不变, 左边安位取反}
// -6
10000110 // 原码
11111001 // 反码
11111010 // 补码 // {原码符号位不变} + {数值位按位取反后 +1}
进行按位操作,都是针对补码去操作。
十进制转二进制
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
64
64 / 2 = 32 -> 0
32 / 2 = 16 -> 0
16 / 2 = 8 -> 1000
// 转为二进制为:100000
// 十进制 33 可以看成是 32 + 1,并且 33 应该是六位二进制的(因为 33 近似 32,而 32 是 2 的五次方,所以是六位),那么 十进制 33 就是 100001,只要是 2 的次方,那么就是 1 否则都为 0
二进制转十进制
二进制 100001 同理,首位是 2^5(1),末位是 2^0(0),相加得出 33(只要是 2 的次方,那么就是 1 否则都为 0)
按位非(~)取反
将 1(原码)转二进制: 00000001
按位取反: 11111110
将符号位之外的其它数字取反 [符号位(即最高位) 为 1(表示负数)]: 10000001
末位加 1 取其补码: 10000010
转换会十进制: -2
00000001 // 原码
11111110 // 按位取反
10000001 // 除符号位取反
10000010 // +1
对任一数值 x 进行按位非操作的结果为 -(x + 1), 例如:2 -> -3
JS 中的作用是配合 indexOf():indexOf 找到一个给定元素的第一个索引,如果不存在,则返回 -1, - 1 取反操作等于 0,其它取反操作不等于 0
if (~arr.indexOf(v)) if (arr.includes(v))
if (~str.indexOf(v)) if (str.indexOf(v) !== -1)
按位或(|)
规则:其中一位为 1,结果就是 1
8 | 7
00001000
00000111
———-
00001111 // 15
任一数值 x 与 0 进行按位或操作,其结果都是 x:
6 | 0
00000110
00000000
———
00000110 // 6
任一数值 x 与 -1 进行按位或操作,其结果都为 -1:
6 | -1
00000110
10000001
———
10000111
将任一数值 x 与 0 进行按位或操作,其结果都是 x。将任一数值 x 与 -1 进行按位或操作,其结果都为 -1
JS 中向下取整 Math.floor, 返回小于或等于一个给定数字的最大整数
num | 0 or Math.floor(num)
Math.floor(45.95); // 45.95 | 0
// 45
Math.floor(45.05); // 45.05 | 0
// 45
Math.floor(4); // 4 | 0
// 4
Math.floor(-45.05); // -45.05 | 0
// -46
Math.floor(-45.95); // -45.95 | 0
// -46
1 | 0 ; // 1
1.1 | 0 ; // 1
‘asfdasfda’ | 0 ; // 0
0 | 0 ; // 0
(-1) | 0 ; // -1
(-1.5646) | 0 ; // -1
[] | 0 ; // 0
({}) | 0 ; // 0
“123456” | 0 ; // 123456
1.23E2 | 0; // 123
1.23E12 | 0; // 1639353344
-1.23E2 | 0; // -123
-1.23E12 | 0; // -1639353344
按位与(&)
规则:每一位都为 1,结果才为 1
8 & 7 // 0
00001000
00000111
00000000 // 0
将任一数值 x 与 0 执行按位与操作,其结果都为 0。将任一数值 x 与 -1 执行按位与操作,其结果都为 x。
JS 中应用:判断奇偶性
10 & 1 // 0 偶数
11 & 1 // 1 奇数
按位异或 (^)
规则:每一位都不同,结果才为 1
8 ^ 7 // 15
1000
0111
1111 // 15
8 ^ 8 // 0
1000
1000
0000 // 0
将任一数值 x 与 0 进行异或操作,其结果为 x。将任一数值 x 与 -1 进行异或操作,其结果为 -x
不进位加法: 根据按位异或的特性就是不进位加法:8 ^ 8 = 0 如果进位了,就是 16 了,所以只需要将两个数进行异或操作,然后进位。那么也就是说两个二进制都是 1 的位置,左边应该有一个进位 1
JS 中应用:交换二个数值
let a = 3
let b = 4
a ^= b
b ^= a
a ^= b
有符号右移 (>>)
将第一个操作数向右移动指定的位数, 向右被移出的位被丢弃,正数则在高位补零,负数则补 1
9 >> 2
00001001 // 移动二位, 以 0 填充
00000010 // 2
-9 >> 2
10001001 // 原码
11110111 // 补码
11111101 // 补码右移
10000011 // 原码 // -3
公式:int v = a / (2 ^ b)
JS 中的应用:任何小数 把它 >> 0 可以取整
9.99 >> 0 // 9
9 >> 0 // 9
9.19 >> 0 // 9
除法运算:
9 >> 1 // 4
8 >> 1 // 4
二分算法中取中间值:
13 >> 1 // 6
12 >> 1 // 6
topic
两个数不使用四则运算得出和
a + b = (a ^ b) + ((a & b) << 1)