位运算及巧妙应用

Posted Leo2Gooo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了位运算及巧妙应用相关的知识,希望对你有一定的参考价值。

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

以上是关于位运算及巧妙应用的主要内容,如果未能解决你的问题,请参考以下文章

Java位运算原理及使用讲解

JS的位运算符“与”(&)及应用

巧妙运用 按位或“|” 运算

Task01:变量运算符数据类型及位运算

MySQL使用位运算

面试题:找出数组中只出现一次的2个数(异或的巧妙应用)(出现3次)