zoukankan      html  css  js  c++  java
  • javascript 位运算

    位运算博大精深,本文总结下基本的位运算的概念。

    1、整数的二进制码###


    位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript中的所有数值都以IEEE-754 64位格式存储,但位操作符并不直接操作64位的值。而是先将64位的值转换成32位的整数,然后执行操作,最后再将结果转换回64位。对于开发人员来说,由于64位存储格式是透明的,因此整个过程就像是只存在32位的整数一样。

    对于有符号的整数,32位中的前31位用于表示整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。这个表示符号的位叫做符号位。例如,数值18的二进制表示是00000000000000000000000000010010,或者更简洁的10010

    负数同样以二进制码存储,但使用的是二进制补码(其实正数也是用补码表示)。计算一个数值的二进制补码,需要经过下列三个步骤:

    1. 求这个数值绝对值的二进制码
    2. 求二进制反码,即将0替换为1,将1替换为0
    3. 得到的二进制反码加1

    比如求-18的二进制码,首先求得18的二进制码:

    0000 0000 0000 0000 0000 0001 0010
    

    然后求其二进制反码:

    1111 1111 1111 1111 1111 1110 1101
    

    最后,二进制反码+1

    1111 1111 1111 1111 1111 1110 1110
    

    这样就求得了-18的二进制表示。

    在ECMAScript中,当对数值应用位操作符时,后台会发生如下转换过程:64位的数值被转换成32位数值,然后执行位操作,最后再将32位的结果转换回64位数值。这样,表面上看起来就好像是在操作32位数值。但这个转换过程也导致了一个严重的负效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0来处理。如果对非数值进行位操作,会先使用Number()函数将该数值转换成一个数值(自动完成),然后再应用位操作,得到的结果是一个数值。

    2、~ & | ^


    接下来介绍4个位操作符。

    按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。

    var num = 25;
    console.log(~num); // -26
    

    对25执行按位非操作,结果得到了-26,这也验证了之前说的结论,“一个负数的二进制码是该数绝对值的反码+1”

    按位与操作符由一个和号字符(&)表示,它有两个操作符数,按位与操作只在两个数值的对应位都是1时才返回1,否则0。

    按位或操作符由一个竖线符号(|)表示,同样也有两个操作符,在有一位是1的情况下返回1,否则0.

    按位异或操作符由一个插入符号(^)表示,在两个数值对应位上只有一个1时返回1,否则0。

    3、<< >> >>>


    左移符号由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。在左移后,原数值右侧空出的位由0填补。

    左移一位其实就相当于将原数值乘以2,左移不会影响操作数的符号位。

    右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位。在移位过程中,空缺位出现在原数值的左侧,符号位的右侧,用符号位的值来填充空位。右移一位相当于原数除2后向下取整。

    无符号右移操作符由三个大于号(>>>)表示,这个操作符会将数值的所有32位都向右移动。对正数来说,无符号右移的结果和有符号相同,但是对负数来说就不一样了,无符号右移会把负数的符号位也进行移动,左边空出位置用0填充。

    var num = -64;
    console.log(num >> 5); // -2
    console.log(num >>> 5); // 134217726 
    

    对-64进行无符号右移操作,将其用二进制码表示:

    1111 1111 1111 1111 1111 1111 1100 0000
    

    右移5位后:

    0000 0111 1111 1111 1111 1111 1111 1110
    

    即十进制的134217726。

    notice:并没有无符号左移!

    4、关于位运算的坑###


    了解的基本的位运算操作后,这里我要谈谈我经常碰到的一个坑。

    int32的取值范围是-2^31 ~ 2^31-1,于是我经常会去求1<<31的值,但是是溢出的...

    console.log(1 << 31); // -2147483648 
    

    原因很简单,1的左边只有30位可以移动,实际上把1移到了符号位上,得到了:

    1000 0000 0000 0000 0000 0000 0000 0000
    

    所以要表示int32的区间范围,可以这样:

    console.log(1 << 31); // -2147483648
    console.log(1 << -1); // -2147483648
    console.log(-0x80000000); // -2147483648
    
    console.log(~(1 << 31)); // 2147483647
    console.log(-(1 << -1) - 1); // 2147483647
    console.log(0x7fffffff); // 2147483647
    console.log(Math.pow(2, 31) - 1); // 2147483647
    console.log((1 << 30) * 2 - 1); // 2147483647
  • 相关阅读:
    Lua metatable & metamethod
    lua 中的点、冒号与self
    Eclipse 快捷键
    logging的使用
    URL转义字符
    UnicodeEncodeError: ‘ascii’ codec can’t encode
    Baidu URL的部分参数
    使用JS伪造Post请求
    print 不换行
    exception keynote
  • 原文地址:https://www.cnblogs.com/lessfish/p/4787145.html
Copyright © 2011-2022 走看看