一、二进制
要了解位运算,先要知道二进制的一些知识
1. 十进制转二进制
2. 二进制转十进制
(这里是从后往前)
3. 原码、反码、补码
// 原码:一个整数,按照绝对值大小转换成的二进制数
13 ===> 0000 1101
// 反码:原码按位取反。
1111 0010
// 补码:正数的补码就是原码,负数的补码是原码的反码再加1
1111 1101
// 看一个二进制是否是正数还是负数,要先看其在计算机中是以有符号进行存储还是无符号进行存储。
// 1.如果是无符号存储,则其为一个正数。
// 2.若是有符号存储,则为补码存储。
// 3.看其最高位,最高位为0,为正数,反之,为负数。
在计算机的世界里,存储的永远是0和1,二进制的世界我们看不到,但是却一直存在。
如何把十进制转化成二进制呢?只需要把十进制一直求余2,然后整除的标0,不整除的标1,从最后一位数往前读,就是该十进制的二进制数了。
在二进制里,负数是怎么存储的呢?
其实二进制的存储分为有符号存储和无符号存储。正数的原码是它本身,反码是各位数取反,补码就是原码。负数的原码也是它本身,反码是位数取反,补码是反码+1
13
原码:0000 1101
反码:1111 0010
补码:0000 1101
-13
原码:1111 1101
反码:0000 0010
补码:0000 0011
二、位运算概览
| 符号 | 描述 | 运算规则 |
|---|---|---|
| & | 与 | 两个位都为1时,结果才为1 |
| | | 或 | 两个位都为0时,结果才为0 |
| ^ | 异或 | 两个位相同为0,相异为1 |
| ~ | 取反 | 0变1,1变0 |
| << | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
| >> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
- &:
/*
两个都为1,结果才是1(这里说的是二进制运算)
25 & 3 = 1
26 & 3 = 2
*/
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
26 = 0000 0000 0000 0000 0000 0000 0001 1010
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0010
- |:
/*
两个都为0,结果才是0(这里说的是二进制运算)
25 | 3 = 1
*/
25 = 0000 0000 0000 0000 0000 0000 0001 1001 // 25 | 3 = 27
3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
- ^:
/*
只有一个数位存放的是 1 时,它才返回 1
25 ^ 3 = 26
26 ^ 3 = 25
*/
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
26 = 0000 0000 0000 0000 0000 0000 0001 1010
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1001
- ~
/*
三步的处理过程:
1. 把运算数转换成 32 位数字
2. 把二进制数转换成它的二进制反码
3. 把二进制数转换成浮点数
*/
var iNum1 = 25; //25 等于 00000000000000000000000000011001
var iNum2 = ~iNum1; //转换为 11111111111111111111111111100110
alert(iNum2); //输出 -26
// 实质上是对数字求负,然后减 1
~undefined // 先转为数字,在求值
- <<: 左移
/*
数字中的所有数位向左移动指定的数量
*/
var iOld = 2; //等于二进制 10
var iNew = iOld << 5; //等于二进制 1000000 十进制 64
2 = 0000 0000 0000 0000 0000 0000 0000 0010
2 << 5 = 0000 0000 0000 0000 0000 0000 0100 0000
/* 简单来说:<< 1 ==> 移动1位 二进制10 相当于 * 2(因为二进制10等于2)
<< 2 ==> 移动2位 二进制100 相当于 * 4(因为二进制100等于4)
<< 3 ==> 移动3位 二进制1000 相当于 * 8(因为二进制1000等于8)
以此类推
移动完了后面缺的数,用0补充
*/
- />>/: 右移
/*
把 32 位数字中的所有数位整体右移,同时保留该数的符号(正号或负号)
数字中的所有数位向右移动指定的数量
*/
// 正数
var iOld = 2; //等于二进制 10
var iNew = iOld >> 2; //等于二进制 10 十进制 2 2 >> 2 = 0
2 = 0000 0000 0000 0000 0000 0000 0000 0010
2 >> 2 = 0000 0000 0000 0000 0000 0000 0100 0000 . 10(移动到这里就没了,但是不保存小数)
注意:操作数的二进制码都是其补码,正数的补码是自身,负数的补码是原码的反码+1
// 负数:左边补1
var iOld = -2; //等于二进制 10
var iNew = iOld >> 2; //等于二进制 10 十进制 2 -2 >> 2 = -1
-2 = 1111 1111 1111 1111 1111 1111 1111 1101
2 >> 2 = 1111 1111 1111 1111 1111 1111 1111 1111
// 需要保持数为负数,所以操作是对负数的二进制位左边补1
反正记住一点:右移到头了,就是-1
常用操作小技巧:
- 除2可以用>>1
- 一般^ | & 后面可以跟0、1