Javascript 位运算
参考:巧用JS位运算
ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。在 ECMAScript 中,所有整数字面量默认都是有符号整数。
数18的表示方法
18 的二进制版本只用了前 5 位,它们是这个数字的有效位。把数字转换成二进制字符串,就能看到有效位:
var iNum = 18;
alert(iNum.toString(2)); //输出 "10010"
正数的源码反码补码相同
负数也存储为二进制代码,不过采用的形式是二进制补码。计算数字二进制补码的步骤有三步:
确定该数字的非负版本的二进制表示(例如,要计算 -18的二进制补码,首先要确定 18 的二进制表示)
求得二进制反码,即要把 0 替换为 1,把 1 替换为 0
在二进制反码上加 1
1. ~
按位取反运算
var v3 = ~8;
console.log(v3);// -9
// 8 的二进制有效位
00...00 1000
// 按位取反后
11...11 0111
//补码-1 在按位取反得到负数数值
00...00 1001'
var v3 = ~-9;
console.log(v3);//8
应用1: 使用按位非~判断索引存在
// 如果url含有?号,则后面拼上&符号,否则加上?号
url += ~url.indexOf("?") ? "&" : "?";
2. &与运算
将进行操作的对应的二进制位分别进行与运算后将结果返回。
var v = 1 & 5;
console.log(v); // 1
应用1:利用& 判断奇偶数
var a =(i)=>i&1?console.log(i+"是奇数"): console.log(i+"是偶数");
应用2:使用按位与&去掉高位
let a = 0b01000110; // 十进制为70
let b = 0b10000101; // 十进制为133
//现在认为他们的高位是没用的,只有低4位是有用的,即最后面4位,为了比较a,b后4位的大小,可以这样比较:
a & 0b00001111 < b & 0b00001111 // true
3.| 或运算
将进行操作的对应的二进制位分别进行或运算后将结果返回。
var v = 1 | 7;
console.log(v);// 7
var v = 1 | 8;
console.log(v);// 9
4. ^异或运算符
将进行操作的对应的二进制位分别进行异或运算后将结果返回。
var a = 1^2;
console.log(a);//3
应用1:使用异或交换两个数
let a = 5,
b = 6;
a = a ^ b;
b = a ^ b; // b 等于 5
a = a ^ b; // a 等于 6
5. << 左移运算符
将操作数对应的二进制进行左移,右侧空出来的用零补填。
var a = 3<<2;
console.log(a);//12
6. >> 右移运算符
有符号右移>> 和 左移运算互为逆运算
var a = 3<<2;
console.log(a);//12
无符号右移>>> 将符号位一起移动
位运算的综合应用
不使用加减乘除来做加法,经常用来考察对位运算的掌握情况。读者可以先自行尝试分析和实现。
不能用加减乘除,意思就是要你用位运算进行计算。以实际例子说明,如a = 81 = 0b1010001,b = 53 = 0b0110101。通过异或运算,我们发现异或把两个数相加但是不能进位,而通过与运算能够知道哪些位需要进位,如下所示:
1010001
^ 0110101
---------
1100100
1010001
& 0110101
---------
0010001
把通过与运算得到的值向左移一位,再和通过异或得到的值相加,就相当于实现了进位,这个应该不难理解。为了实现这两个数的相加可以再重复这个过程:先异或,再与,然后进位,直到不需要再进位了就加完了。所以不难写出以下代码:
function addByBit(a, b) {
if (b === 0) {
return a;
}
// 不用进位的相加
let c = a ^ b;
// 记录需要进位的
let d = a & b;
d = d << 1;
// 继续相加,直到d进位为0
return addByBit(c, d);
}
let ans = addByBit(5, 8);
console.log(ans);
位运算还经常用于生成随机数、哈希,例如Chrome对字符串进行哈希的算法是这样的:
uint32_t StringHasher::AddCharacterCore(uint32_t running_hash, uint16_t c) {
running_hash += c;
running_hash += (running_hash << 10);
running_hash ^= (running_hash >> 6);
return running_hash;
}
不断对当前字符串的ASCII值进行累加运算,里面用到了异或,左移和右移。