位运算及巧妙应用

2021/5/20 18:26:45

本文主要是介绍位运算及巧妙应用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

  • 1. 位运算
  • 2. 位运算的特点
  • 3. 位运算应用场景举例

1. 位运算

JavaScript中的数字以浮点数的形式64位存储。但在位运算中,数字被转换为有符号32位整数格式。每种位操作均直接在这32位数上实现结果,返回值也是有符号的32位整数。最后再将这32位的结果转换回64位数值。

这种转换导致了一个严重的副效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0处理。

如果对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后再应用位操作。得到的结果将是一个数值。

js中的位运算有 按位或(|)按位与(&)按位非(~)按位异或(^)左移(<<)有符号右移(>>)无符号右移(>>>)。注意与逻辑运算符进行区分。

位运算的优先级很低,注意需要加上括号增加优先级。

  1. 按位或(|)

    规则:有1为1,全0为0

    这里举一个或运算的例子,两个操作数一正一负(正数的二进制直接以原码的形式存储,负数以补码的形式存储)。

  2. 按位与(&)

    规则:有0为0,全1为1

  3. 按位非(~)

    规则:返回数值的反码

    本质:操作数的负值减1(-x-1)

  4. 按位异或(^)

    规则:相同为0,不同为1

  5. 左移(<<)

    规则:数值的所有位向左移动指定的位数,右侧的空位用0补齐

    特点:每左移一位,原操作数乘以一个2

    借此,我们可以利用有符号右移来代替所有的乘2或乘2^n运算。

    注意: 左移不会影响操作数的符号位

  6. 有符号右移(>>)

    规则:将数值向右移动,但保留符号位,左侧空位用符号位补齐

    特点:每右移一位,原操作数除以一个2

    借此,我们可以利用有符号右移来代替所有的除2乘2或除2^n运算。

  7. 无符号右移(>>>)

    规则:将数值所有32位都向右移动,左侧空位用0补齐

    注意: 无符号右移操作会将负数的二进制码(补码)当成正数的二进制码,因此结果往往很大。而该操作会将左侧空位用0补齐,因此结果为正数。

2. 位运算的特点

位运算符较快 —— 当进行数学运算的时候,位运算操作比任何布尔运算或者算术运算快。选择性地用位运算替换算术运算可以极大地提升复杂运算的性能。

同时,位运算也有着让代码可读性变差的缺点。

3. 位运算应用场景举例

  1. 判断奇偶 —— 按位与(&)

    由于偶数的最低位是0,奇数的最低位是1。那么如果一个数是偶数,那么它与1(二进制中只有最后一位为1)进行位“与”操作的结果就是0,相反如果是奇数的话,结果就是1。

    利用这个特性,我们可以利用num & 1来判断奇偶。

    return num & 1 === 1 ? '奇数' : '偶数';
    

    利用位运算使这段代码的运算速度比原始代码提升了约50%。

  2. 不借助第三个变量实现两个变量的交换 —— 按位异或(^)

    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    

    当然也可以借助ES6语法:

    [a, b] = [b, a]
    
  3. 判断数组中某项是否存在 —— 按位取反(~)

    利用有且仅有-1取反为0:~-1 === 0

    这个我们通常用在数组的indexOf方法中。

    之前,我们是这样写的:

    if(arr.indexof(item) > -1) {
        // TODO
    }
    

    现在,可以这么写:

    if(~arr.indexof(item)) {
        // TODO
    }
    

    事实上,利用x取反的实质是-x-1这个特性,我们可以利用按位取反替换任何绝对值不大于2^32的整数。

  4. 取整

    注意: 对于大于2的32次方的整数,大于32位的数位都会被舍去。

    由于0与任何数相或,都不会改变原数,而位运算会将数字转为整数,因此我们可以直接将操作数与0相或:

    num | 0
    

    同样,我们还可以给num取两次反:

    ~~num
    

    或者左移0位/有符号右移0位:

    num >> 0
    num << 0
    
  5. 位掩码判断用户权限

    使用位操作的技术称为位掩码。位掩码在计算机科学中是一种常用的技术,可用于同时判断多个布尔选项,快速的将数字转换成布尔标志数组。掩码中每个选项的值都是2的幂。

    例如,我们来判断用户是否拥有某项权限:

    const writable = 1;  // 可写
    const readable = 2;  // 可读
    const executable = 4;  //可执行
    
    const currentUser = writable | readable;  // 当前用户可读、写
    
    if(currentUser | writable) { // 如果当前用户可写则执行代码
        // TODO
    }
    
    if(currentUser | readable) { // 如果当前用户可读则执行代码
        // TODO
    }
    
  6. 边界判断

    假如我们有一个拖动事件,规定被拖动模块需要在容器内部运动,这时就有边界判断,这其中又包括上,下,左,右四种单一边界,同时还有类似上右,上左等叠加边界,如果我们需要记录这种状态,通过位运算要比使用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
    }
    
  7. 判断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



这篇关于位运算及巧妙应用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程