美文网首页JavaScript
[JS] 有符号整数的位操作

[JS] 有符号整数的位操作

作者: 何幻 | 来源:发表于2018-04-25 11:50 被阅读14次

1. 32位有符号整数

按位操作符(Bitwise operators)会使用内置函数,7.1.5 ToInt32 ( argument )
先将其操作数转换成32位有符号整数,再进行位操作,最后返回一个32位有符号整数。

包括,
12.5.8 Bitwise NOT Operator ( ~ )
12.9.3 The Left Shift Operator ( << )
12.9.4 The Signed Right Shift Operator ( >> )
12.12 Binary Bitwise Operators

因此,a | 00 | a,都可以将变量a中数值转换为32位有符号整数。
某些特殊的值,并不是32位有符号整数的安全范围,它们会被转换为0

特殊值 ToInt32
-0 0
NaN 0
Infinity 0
-Infinity 0

2. 补码编码

在计算机中表示有符号整数,通常使用补码(two's-complement)进行编码。
它将字的最高有效位解释为符号位,符号位被置为1时,表示值为负,
符号位被置为0时,表示值为非负。

因此,字长为4的二进制数0001表示整数1,其中0*2^3+0*2^2+0*2^1+1*2^0=1
1111就表示整数-1,其中-1*2^3+1*2^2+1*2^1+1*2^0=-1

负数的补码,还可以按照“逐位取反后,加一”的方式来获取相应的整数值。
例如,1111逐位取反0000,然后再加一0001,它是1的二进制表示,
因此1111就是表示-1了。

3. ~x === -(x+1)

~操作符,它首先将操作数转换成32位有符号整数,然后再按位取反。

例如,1的32位补码编码为,

00000000 00000000 00000000 00000001

按位取反,

11111111 11111111 11111111 11111110

它表示什么呢?

先看最高为的符号位,是1,它表示一个负数,
然后“逐位取反后,加一”,00000000 00000000 00000000 00000002值为2
因此,11111111 11111111 11111111 11111110表示-2

一般的,可以证明
对于任意的32位有符号整数x来说,~x === -(x+1)

详细证明见文后的附录。

4. 数组的索引范围

ECMAScript中,数组元素的索引范围是,0Math.pow(2,32)-2

规范9.4.2 Array Exotic Objects中指出,

Every Array object has a length property whose value is always a nonnegative integer less than 2^32.
因此,数组length的最大值是Math.pow(2,32)-1

a = [];
a.length = Math.pow(2,32)-1;

a;  // [empty × 4294967295]

a.length = Math.pow(2,32);
// Uncaught RangeError: Invalid array length

超过数组length的索引,会被看做数组的属性值,

a = [];
a.length = Math.pow(2,32)-1;  
// 最后一个元素的索引是Math.pow(2,32)-2

a;  // [empty × 4294967295]

// 再为数组添加一个更大索引值的元素
a[Math.pow(2,32)-1] = 1;
a;  // [empty × 4294967295, 4294967295: 1]

因此,indexOf返回的最大值为Math.pow(2,32)-2

5. indexOf

Array.prototype.indexOf
会返回给定数组元素在数组中的索引,如果找不到给定元素,就返回-1

因为只有~-1等于0,其他索引值取反都非0
所以,人们经常使用!~a.indexOf(element)来判断元素是否在数组中。

这里有一个值得注意的事情,由于~会首先将操作数转换成32位有符号整数,
所以,-1Math.pow(2,32)-1具有相同的编码,

11111111 11111111 11111111 11111111

但是,数组的最大索引为Math.pow(2,32)-2,小于上面这个值,
因此,对indexOf返回的值进行取反,除了-1之外,总是非0值,是安全的做法。


附录

下面给出~x === -(x+1)的证明。

(1)先看正整数
对于32位正整数来说,它的二进制编码为,

0nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn

其中n表示0或者1
则,~x为,

1uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu

其中un的取反结果。

以上二进制表示,如果看成32位有符号整数,则由于符号位1,它是一个负数,
其绝对值为,“逐位取反后,加一”,即为,(0nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn)+1 === x+1
即,-(x+1)

因此,对于正数,~x === -(x+1)

(2)再看负整数
对于32位负整数来说,它的二进制编码为,

1nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn

其中n表示0或者1
则,~x为,

0uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu

其中un的取反结果。

0uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu的值为t
1nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn的值为,-(t+1),(逐位取反后,加一)。

因此,x === -(t+1)~x === t
即,~x === t === -(-(t+1) + 1) === -(x+1)

证毕。

参考

你不知道的JavaScript(中卷)
ECMAScript Language Specification
深入理解计算机系统

相关文章

  • [JS] 有符号整数的位操作

    1. 32位有符号整数 按位操作符(Bitwise operators)会使用内置函数,7.1.5 ToInt32...

  • Cpp:位操作符

    位操作符:位操作符操作的整数可以是有符号或无符号数。 下面的例子,假设unsigned char有8位: ~: 类...

  • Javascript 位运算及运用

    Javascript 位运算 参考:巧用JS位运算 ECMAScript 整数有两种类型,即有符号整数(允许用正数...

  • scala编程实战笔记(2-数值)

    Char: 16位无符号UnicodeByte: 8位有符号整数Int: 32有符号整数Long: 64有符号整...

  • 位运算符

    位运算符 只对整数有效 以32位带符号的整数进行运算,返回值也是一个32位带符号的整数 有符号整数以31位表示整数...

  • js实现无符号整数按位`取反`

    什么是无符号整数和有符号整数? 有符号就是最高位是符号位,其余的位是数据位。无符号就是所有位都是数据位。比如cha...

  • Swift 中的数值类型

    整数 Swift 提供了 8,16,32 和 64 位编码的有符号和无符号整数 命名方式:例如 8 位无符号整数的...

  • swift基本数据类型

    整数 Swift 提供了 8,16,32 和 64 位编码的有符号和无符号整数 命名方式:例如 8 位无符号整数的...

  • C++ 术语

    类型 int8/ uint8 :8位有符号/无符号整数 int16/ uint16 :16位有符号/无符号整数 i...

  • leecode 7反转整数

    给定一个 32 位有符号整数,将整数中的数字进行反转假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−...

网友评论

    本文标题:[JS] 有符号整数的位操作

    本文链接:https://www.haomeiwen.com/subject/hhwelftx.html