位运算
JS 中有三个位移运算:
<<:左移>>:右移>>>:无符号右移
我们直接看一下 Demo:
console.log(2 << 1); // 4
console.log(2 >> 1); // 1
console.log(2 >>> 1); // 1
console.log(-2 << 1); // -4
console.log(-2 >> 1); // -1
console.log(-2 >>> 1); // 2147483647
正数位移运算
乍一眼看到上面 Demo 的打印结果,你应该是懵逼的,接下来我来解释一下这个结果到底是如何运算出来的。 上面的 Demo 中有 “2” 和“-2”,这是两个十进制数,并且是 int 类型的(java 中占四个字节),位运算是基于二进制 bit 来的,所以我们需要将十进制转换为二进制之后再进行运算:
- 2 <<1:十进制 “2” 转换成二进制为“00000000 00000000 00000000 00000010”,再将二进制左移一位,高位丢弃,低位补 0,所以结果为“00000000 00000000 00000000 00000100”,换算成十进制则为“4”
- 2 >> 1:十进制 “2” 转换成二进制为“00000000 00000000 00000000 00000010”,再将二进制右移一位,低位丢弃,高位补 0,所以结果为“00000000 00000000 00000000 00000001”,换算成十进制则为“1”
对于这两种情况非常好理解,那什么是无符号右移,以及负数是怎么运算的呢? 我们先来看 - 2 <<1 与 - 2>> 1,这两个负数的左移与右移操作其实和正数类似,都是先将十进制数转换成二进制数,再将二进制数进行移动,所以现在的关键是负数如何用二进制数进行表示。
负数位移运算
我们再来看 - 2 <<1 与 - 2>> 1。
-2 用原码表示为 10000000 00000000 00000000 00000010
-2 用反码表示为 11111111 11111111 11111111 11111101
-2 用补码表示为 11111111 11111111 11111111 11111110
-2 << 1,表示 - 2 的补码左移一位后为 11111111 11111111 11111111 11111100,该补码对应的反码为
11111111 11111111 11111111 11111100
- 1
= 11111111 11111111 11111111 11111011
该反码对应的原码为:符号位不变,其他位取反,为 10000000 00000000 00000000 00000100,表示 - 4。 所以 - 2 << 1 = -4。 同理 - 2 >> 1 是一样的计算方法,这里就不演示了。
无符号右移
上面在进行左移和右移时,我有一点没讲到,就是在对补码进行移动时,符号位是固定不动的,而无符号右移是指在进行移动时,符号位也会跟着一起移动。 比如 - 2 >>> 1。 -2 的补码右移 1 位为:01111111 11111111 11111111 11111111 右移后的补码对应的反码、原码为:01111111 11111111 11111111 11111111 (因为现在的符号位为 0,表示正数,正数的原、反、补码都相同) 所以,对应的十进制为 2147483647。 也就是 - 2 >>> 1 = 2147483647
总结
这里总结一下,我们可以发现:2 << 1 = 4 = 2* 22 << 2 = 8 = 2* 2* 2 2 << n = 2* 2^n m << n = m * 2^n
右移则相反,所以大家以后在源码中再看到位运算时,可以参考上面的公式。
