计算机中的数在内存中都是以二进制形式进行存储的,用位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。
位操作符
& 与运算 两个位都是 1 时,结果才为 1,否则为 0,如
1 0 0 1 1
& 1 1 0 0 1
------------------------------
1 0 0 0 1
| 或运算 两个位都是 0 时,结果才为 0,否则为 1,如
1 0 0 1 1
| 1 1 0 0 1
------------------------------
1 1 0 1 1
^ 异或运算,两个位相同则为 0,不同则为 1,如
1 0 0 1 1
^ 1 1 0 0 1
-----------------------------
0 1 0 1 0
~ 取反运算,0 则变为 1,1 则变为 0,如
~ 1 0 0 1 1
-----------------------------
0 1 1 0 0
<< 左移运算,向左进行移位操作,高位丢弃,低位补 0,如
int a = 8;
a << 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000
移位后:0000 0000 0000 0000 0000 0000 0100 0000
>> 右移运算,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位,如
unsigned int a = 8;
a >> 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000
移位后:0000 0000 0000 0000 0000 0000 0000 0001
int a = -8;
a >> 3;
移位前:1111 1111 1111 1111 1111 1111 1111 1000
移位前:1111 1111 1111 1111 1111 1111 1111 1111
我们可能很少在编程中用位运算,如果没深入学习,可能也很难理解。平时的数值运算,其实是要先转换成二进制再进行运算的,而位运算就是直接进行二进制运算,所以位运算的执行效率肯定是更高的。下面通过一些实例来加深对位运算的理解。
按位与(&)
&&运算符我们都知道,只有两个都为真,结果才为真。&道理是一样的,只有两个数的值为1时,才返回1。例如1和3的按位与操作:
0001&0011---------0001
只有对应的数为1时,结果才为1,其他都为0。
判断一个数是奇数还是偶数,我们会用求余数来判断:
functionassert(n){if(n %2===1) { console.log("n是奇数"); }else{ console.log("n是偶数"); }}assert(3); //"n是奇数"
我们也可以用一个数和1进行按位&操作来判断,而且速度更快:
functionassert(n){if(n &1) { console.log("n是奇数");}else{ console.log("n是偶数");}}assert(3); //"n是奇数"
下面是位运算过程:
1=00013=0011--------& =0001
奇数的二进制码的最后一位数肯定是1,而1只有最后一位为1,按位&操作之后,结果肯定只有最后一位数为1。而偶数的二进制表示的最后一位数是0,和1进行按位&操作,结果所有位数都为0。
按位或(|)
|与||操作符的道理也是一样的,只要两个数中有一个数为1,结果就为1,其他则为0。
0001|0011---------0011
对浮点数向下求整,我们会用下面的方法:
varnum =Math.floor(1.1);// 1
我们也可以用位运算来求整:
varnum=1.1|0;// 1
其实浮点数是不支持位运算的,所以会先把1.1转成整数1再进行位运算,就好像是对浮点数向下求整。所以1|0的结果就是1。
按位非(~)
按位非就是求二进制的反码:
varnum=1;// 二进制 00000000000000000000000000000001varnum1 = ~num;// 二进制 11111111111111111111111111111110
我们知道,js中的数字默认是有符号的。有符号的32位二进制的最高位也就是第一位数字代表着正负,1代表负数,0代表整数。那到底11111111111111111111111111111110等于多少呢?最高位为1代表负数,负数的二进制转化为十进制:符号位不变,其他位取反加1。取反之后为10000000000000000000000000000001,加1之后为10000000000000000000000000000010,十进制为-2。
按位异或(^)
按位异或是两个数中只有一个1时返回1,其他情况返回0。
0001^0011---------0010
数字与数字本身按位异或操作得到的是0,因为每两个对应的数字都相同,所以最后返回的都是0。
我们经常会需要调换两个数字的值:
var num1 = 1, num2 = 2, temp;
temp = num1;
num1 = num2; // 2
num2 = temp; // 1
如果装逼一点的话,可以这样:
var num1 = 1, num2 = 2;num1 = [num2, num2 = num1][0];console.log(num1); // 2console.log(num2); // 1
如果想再装的稳一点的话,可以这样:
varnum1 =1, num2 =2;num1 ^= num2;// num1 = num1 ^ num2 = 1 ^ 2 = 3num2 ^= num1;// num2 = num2 ^ (num1 ^ num2) = 2 ^ (1 ^ 2) = 1num1 ^= num2;// num1 = num1 ^ num2 = 3 ^ 1 = 2console.log(num1);// 2console.log(num2);// 1
有符号左移(<<)
有符号左移会将32位二进制数的所有位向左移动指定位数。如:
varnum=2;// 二进制10num=num<<5;// 二进制1000000,十进制64
如果要求2的n次方,可以这样:
functionpower(n){return1<< n;}power(5);// 32
1的二进制是01,左移5位就是0100000,十进制就是2的5次方32。
有符号右移(>>)
有符号右移会将32位二进制数的所有位向右移动指定位数。如:
varnum=64;// 二进制1000000num=num>>5;// 二进制10,十进制2
求一个数的二分之一:
var num =64>> 1;//32
有符号左移与右移不会影响符号位。
无符号右移(>>>)
正数的无符号右移与有符号右移结果是一样的。负数的无符号右移会把符号位也一起移动,而且无符号右移会把负数的二进制码当成正数的二进制码:
varnum=-64;// 11111111111111111111111111000000num=num>>>5;// 134217726
所以,我们可以利用无符号右移来判断一个数的正负:
functionisPos(n){return(n === (n >>>0)) ?true:false; }isPos(-1);// falseisPos(1);// true
-1>>>0虽然没有向右移动位数,但-1的二进制码已经变成了正数的二进制码:
11111111111111111111111111111111
所以-1>>>0的值为4294967295。
~~用来取整
~~3.5 3
~~-2.6 -2