位运算及巧妙应用
Posted Leo2Gooo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了位运算及巧妙应用相关的知识,希望对你有一定的参考价值。
1. 位运算
javascript中的数字以浮点数的形式64位存储。但在位运算中,数字被转换为有符号32位整数格式。每种位操作均直接在这32位数上实现结果,返回值也是有符号的32位整数。最后再将这32位的结果转换回64位数值。
这种转换导致了一个严重的副效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0处理。
如果对非数值应用位操作符,会先使用Number()
函数将该值转换为一个数值(自动完成),然后再应用位操作。得到的结果将是一个数值。
js中的位运算有 按位或(|)
,按位与(&)
,按位非(~)
,按位异或(^)
,左移(<<)
,有符号右移(>>)
,无符号右移(>>>)
。注意与逻辑运算符进行区分。
位运算的优先级很低,注意需要加上括号增加优先级。
按位或(|)
规则:有1为1,全0为0
这里举一个或运算的例子,两个操作数一正一负(正数的二进制直接以原码的形式存储,负数以补码的形式存储)。
按位与(&)
规则:有0为0,全1为1
按位非(~)
规则:返回数值的反码
本质:操作数的负值减1(-x-1)
按位异或(^)
规则:相同为0,不同为1
左移(<<)
规则:数值的所有位向左移动指定的位数,右侧的空位用0补齐
特点:每左移一位,原操作数乘以一个2
借此,我们可以利用有符号右移来代替所有的乘2或乘2^n运算。
注意: 左移不会影响操作数的符号位
有符号右移(>>)
规则:将数值向右移动,但保留符号位,左侧空位用符号位补齐
特点:每右移一位,原操作数除以一个2
借此,我们可以利用有符号右移来代替所有的除2乘2或除2^n运算。
无符号右移(>>>)
规则:将数值所有32位都向右移动,左侧空位用0补齐
注意: 无符号右移操作会将负数的二进制码(补码)当成正数的二进制码,因此结果往往很大。而该操作会将左侧空位用0补齐,因此结果为正数。
2. 位运算的特点
位运算符较快 —— 当进行数学运算的时候,位运算操作比任何布尔运算或者算术运算快。选择性地用位运算替换算术运算可以极大地提升复杂运算的性能。
同时,位运算也有着让代码可读性变差的缺点。
3. 位运算应用场景举例
判断奇偶 —— 按位与(&)
由于偶数的最低位是0,奇数的最低位是1。那么如果一个数是偶数,那么它与1(二进制中只有最后一位为1)进行位“与”操作的结果就是0,相反如果是奇数的话,结果就是1。
利用这个特性,我们可以利用num & 1来判断奇偶。
return num & 1 === 1 ? \'奇数\' : \'偶数\';
利用位运算使这段代码的运算速度比原始代码提升了约50%。
不借助第三个变量实现两个变量的交换 —— 按位异或(^)
a = a ^ b;
b = a ^ b;
a = a ^ b;当然也可以借助ES6语法:
[a, b] = [b, a]
判断数组中某项是否存在 —— 按位取反(~)
利用有且仅有-1取反为0:~-1 === 0
这个我们通常用在数组的indexOf方法中。
之前,我们是这样写的:
if(arr.indexof(item) > -1) {
// TODO
}现在,可以这么写:
if(~arr.indexof(item)) {
// TODO
}事实上,利用x取反的实质是-x-1这个特性,我们可以利用按位取反替换任何绝对值不大于2^32的整数。
取整
注意: 对于大于2的32次方的整数,大于32位的数位都会被舍去。
由于0与任何数相或,都不会改变原数,而位运算会将数字转为整数,因此我们可以直接将操作数与0相或:
num | 0
同样,我们还可以给num取两次反:
~~num
或者左移0位/有符号右移0位:
num >> 0
num << 0位掩码判断用户权限
使用位操作的技术称为位掩码。位掩码在计算机科学中是一种常用的技术,可用于同时判断多个布尔选项,快速的将数字转换成布尔标志数组。掩码中每个选项的值都是2的幂。
例如,我们来判断用户是否拥有某项权限:
const writable = 1; // 可写
const readable = 2; // 可读
const executable = 4; //可执行
const currentUser = writable | readable; // 当前用户可读、写
if(currentUser | writable) { // 如果当前用户可写则执行代码
// TODO
}
if(currentUser | readable) { // 如果当前用户可读则执行代码
// TODO
}边界判断
假如我们有一个拖动事件,规定被拖动模块需要在容器内部运动,这时就有边界判断,这其中又包括上,下,左,右四种单一边界,同时还有类似上右,上左等叠加边界,如果我们需要记录这种状态,通过位运算要比使用if判断要简单一些,上右下左四种边界分别用1,2,4,8表示,代码如下:
let flag = 0;
if (pos.left < left) flag = flag | 8; // 左
if (pos.bottom > bottom) flag = flag | 4; // 下
if (pos.right > right) flag = flag | 2; // 右
if (pos.top < top) flag = flag | 1; // 上
switch(flag) {
// 上
// 0 | 1
case 1:
// 右
// 0 | 2
case 2:
// 右上
// 0 | 1 | 2
case 3:
// 下
// 0 | 4
case 4:
// 右下
// 0 | 2 | 4
case 6:
// 左
// 0 | 8
case 8:
// 左上
// 0 | 8 | 1
case 9:
// 左下
// 0 | 8 | 4
case 12:
// code
}判断2的n次幂
我们知道,如果一个数num是2的n次幂,那么num的二进制数一定存在这个规律:
对于num,第一位是1,其余为都为0 对于num-1,第一位是0,其余都为1
例如,8(1000),7(0111),那么8 & 7一定为0。根据这个规律我们可以这么判断2的n次幂:
num & (num - 1) === 0 ? true : false;
————————————————
版权声明:本文为CSDN博主「張猴子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_42532128/article/details/106287345
以上是关于位运算及巧妙应用的主要内容,如果未能解决你的问题,请参考以下文章