位运算的奇淫技巧

211 阅读2分钟

任何编程语言都提供了二进制数据的与、或、非位操作逻辑,JavaScript也不例外。下面首先介绍JS中的位运算接口(位运算符)

位运算符

操作用法描述
按位与a & b在a,b的位表示中,每一个对应的位都为1则返回1,否则返回0
按位或a | b在a,b的位表示中,每一个对应的位,只要有一个为1则返回1,否则返回0
按位异或a ^ b在a,b的位表示中,每一个对应的位,两个不相同则返回1,相同则返回0
按位非~a反转被操作数的位
左移a << b将a的二进制串向左移动b位,右边移入0
算术右移a >> b把a的二进制表示向右移动b位,丢弃被移出的所有位
无符号右移a >>> b把a的二进制表示向右移动b位,丢弃被移出的所有位,并把左边空出的位都填充为0

引用至表达式与运算符 - JavaScript | MDN

算法中位运算符的妙用

求中位数

在二分法中,往往有 middle = (left + right) / 2 的情况发生,并且还需要判断 middle 是否是带0.5的小数,而往往都会固定向上或向下取整,需要用到 floor() 方法,位运算符 >> 可以很方便运算效率更快地解决。

// 合为偶数时
let left = 10, right = 90;
let middle = (left + right) >> 1;
console.log(middle) // 50
// 合为奇数时
let left = 10, right = 91;
let middle = (left + right) >> 1;
console.log(middle) // 50

变量交换

很多语言中的变量交换都是经典的temp = a; a = b; b = temp,在 python 语言中, 变量交换可以直接 a, b = b, a,在ES6的解构赋值中,JavaScript 也可以很方便地进行变量交换:[a, b] = [b, a],除此之外,变量还可以通过异或运算符交换:

let a = 123;
let b = 456;
a ^= b;
b ^= a;
a ^= b;
console.log(a, b); // 456 123

判断奇偶

奇数最右位为1,偶数为0,因此

  • 奇数 & 1 = 1
  • 偶数 & 1 = 0
console.log(5 & 1); // 1
console.log(4 & 1); // 0

使用注意事项

  • 在所有位运算操作中,运算的变量如果不是 number 类型,则会隐式转换为该类型
const pureString = 'hello world!'
console.log(~pureString) // -1
const interString = '5'
console.log(~interString) // -6
const boolTrue = true
console.log(~boolTrue) // -2

notice: 之所以会出现~5 === -6的情况,是因为负数是以补码的形式存储的,关系有:负数 === ~正数+1

  • 如果变量是number类型且为浮点数,则小数部分丢失
const floatNumber = 13.14
console.log(~floatNumber) // -14
console.log(floatNumber >> 0) // 13
console.log(floatNumber | floatNumber) // 13

可以利用此特性来轻松完成一些特殊的需求:

let a = 13.14
console.log(a >> 0) // 13
a = -13.14
console.log(a >> 0) // -13
// 如果用Math.floor(),效果不一样
let b = 13.14
console.log(Math.floor(b)) // 13
b = -13.14
console.log(Math.floor(b)) // -14 <- difference