转:http://www.hehe0.com/javascript-wei-yun-suan-xiao-ji/
Javascript 位运算小记
位运算属于数字底层计算,运算过程比较复杂. 对大部分JSer来说是生涩难懂,能不用尽量不用. 恰当应用位运算符,可以帮我们轻松实现一些复杂的计算,节省大量脚本代码.
废话不多说,我们现在开始一起来学习位运算: 先来了解整数的存储方式: 在JavaScript中,所有整数字面量默认都是有符号的整数。 我们用32位的二进制表示整数: 前31位表示整数的数值,用第32位表示整数的符号,0表示正数,1表示负数。 正数与负数在表示方式上有所不同.
比如数值18,转换为二进制为10010,那么其存储形式表现为: 0000 0000 0000 0000 0000 0000 0001 0010 而数值-18,也存储为二进制代码,不过采用的形式是二进制补码(具体转换方式可以google搜索一下): 1111 1111 1111 1111 1111 1111 1110 1110
我们所说的位运算,就是在数值的32位二进制形式上,对各个位数进行的计算. JavaScript支持的位运算符有: ~(NOT), &(AND), |(OR), ^(XOR), <<(左移), >>(有符号右移), >>>(无符号右移)
~(NOT) 把运算数转换为32位数字–>转换为反码–>转换为浮点数 实质上是对数字求负,然后减1: ~25=-26,~-10=9
&(AND)是对每个数字中的数位对齐,同一位置的两个数位进行”与”运算 25=0000 0000 0000 0000 0000 0000 0001 1001 3=0000 0000 0000 0000 0000 0000 0000 0011 ———————————————————————————————————————————— &=0000 0000 0000 0000 0000 0000 0000 0001 = 1
|(OR) 是对每个数字中的数位对齐,同一位置的两个数位进行”或”运算 25=0000 0000 0000 0000 0000 0000 0001 1001 3=0000 0000 0000 0000 0000 0000 0000 0011 ———————————————————————————————————————————— |=0000 0000 0000 0000 0000 0000 0001 1011 = 27
^(XOR) 是对每个数字中的数位对齐,同一位置的两个数位进行”异或”运算 25=0000 0000 0000 0000 0000 0000 0001 1001 3=0000 0000 0000 0000 0000 0000 0000 0011 ———————————————————————————————————————————— ^=0000 0000 0000 0000 0000 0000 0001 1010 = 26
<<(左移) 是把所有位数(除了符号位)向左移制定数量,空位用0填充; 2=0000 0000 0000 0000 0000 0000 0000 0010 ———————————————————————————————————————————— <<5=0000 0000 0000 0000 0000 0000 0100 0000=64
>>(有符号位右移) 是把所有位数(除了符号位)向右移制定数量,空位用0填充; 64=0000 0000 0000 0000 0000 0000 0100 0000 ———————————————————————————————————————————— >>5=0000 0000 0000 0000 0000 0000 0000 0010=2
>>>(无符号位右移) 是把所有位数(包括符号位)向右移制定数量,空位用0填充; 正数运行结果跟有符号位右移是一样的, 而对于负数的无符号右移,总是会得到一个非常大的数字.所以使用时候要特别小心.
说了这么多,我们还是很难知道位运算在实际开放在应该怎么运用. 以下是我收集整理的一些运用技巧: 1.位运算比直接乘除计算速度快(因数必须是2的幂的数): << ==* >> ==/ n==2n 例:24<<2==24*4 32>>3==32/8 规律是:左操作数 <<(>>) (右操作数2的幂数)
2.随机取整
var rnd=Math.random(); alert("a:"+((rnd*5)|0));//rang=0-4 alert("a:"+((rnd*5)|1));//rang=1-5 |
3.奇偶数判断
for(var i=0;i<10;i++){ if(i&1){ alert("num is odd!"); } } |
4.浮点数取整 浮点数是不支持位运算的。在JS中浮点数参与位运算一定会被转换成32为整数
if(num>=0){ num|0==Math.floor(num); }else{ num|0==Math.ceil(num); } |
5.num & -2就可以用于取得不大于num,最近的一个偶数 -7 & -2 = -8 11 & -2 = 10
6.与2的幂的数做模运算 17 % 4 == 17 & (4-1) = 1 规律是:左操作数 & (右操作数2的幂数-1)。
7.得到不小于本身最近的奇数 -34 | 1 = -33、34 | 1 = 35
8.奇数 ^ 1,可以得到不大于本身最近的偶数,而偶数 ^ 1,可以得到不小于自身最近的奇数。
9.^还有一个特性,就是两次对同一个数作异或会还原, 也就是说a ^ b ^ b = a
由此可以实现a,b值对调:
var a = 1, b = 2; //开始对调 a ^= b; //a = 1 ^ 2; b ^= a; //b = (a ^ b) ^ b = a = 1; a ^= b; //a = (a ^ b) ^ (a) = b ^ a ^ a = b = 2; //对调完成 //其实只要是一对相反的操作 a += b; //a = a + b; 加法符号交换律 b = a - b; //b = (a+b) - b = a; 这里由于+ -不符合交换律 a -= b; //a = (a+b) - a = b; //发挥你的想象力吧! |
10.取反数:-num=~num+1;
11.判断一个二进制数里面的1的总数是奇数还是偶数,可以这样:
var num = 1234567, b = 1; while (b < 17) { num ^= (num >> b); b <<= 1; } alert(num & 1);//1,说明有奇数个1 num.toString(2); //100101101011010000111,一共11个1 |
简单解释下: 0100101101011010000111 ① 注意这里由于1234567是个正数,所以补码和原码一样 ^ 0010010110101101000011 ② 右移1位 = 0110111011110111000100 ③ 这个数字的从右边数起第一位代表了①从右边数起前两位中1的个数的奇偶性(1为奇,0为偶) 0110111011110111000100 ① ^ 0001101110111101110001 ② 右移2位 = 0111010101001010110101 ③ 这个数字的从右边数起第一位代表①从右边数起前四位中1的个数的奇偶性 …以此类推,到了第五次操作后的num,最后一位就代表了原数整个32位中有1个数的奇偶性。
12.计算二进制数中1的个数:
var x = 1234567; x = (x & 0x55555555) + ((x >> 1) & 0x55555555); x = (x & 0x33333333) + (( x >> 2 ) & 0x33333333); x = (x & 0x0F0F0F0F) + (( x >> 4 ) & 0x0F0F0F0F); x = (x & 0x00FF00FF) + (( x >> 8 ) & 0x00FF00FF); x = (x & 0x0000FFFF) + (( x >> 16 ) & 0x0000FFFF); alert(x); //11 |
典型的分治法,不要看了二进制就忘了分治法了 第一步我先把原数的补码分成2个起共16份,数出里面1的个数,最多就是10即2个。第二部就是合并第一步中的2分份,那就直接做加法,以此类推…
13.求绝对值可以这样: (x ^ (x >> 31)) – (x >> 31); 这里x >> 31就是取符号位,如果是正数,那补码也是原码,所以x >> 31位0,而(x^0) – 1显然为x。 如果是负数,那么x^1,就相当于按位取反,这时得到一个正数的补码,由于原数的原码到补码过程是加了1的,所以这里要减去1,即(x^1) – 1。
14.a*b的正负性就和a^b一致
位运算还可以做什么事情呢?那就等着你们去挖掘了. 最后别忘了,运用才是王道.