这两天在写 Electron 的时候发现了一个很诡异的错误。程序前两天测试的时候没有问题,今天突然不行了。检查代码发现错误原因是这样的:
1 | buf.writeUint32BE(now & 0xffffffff, idx) |
其中buf
是一个Buffer
对象,now
是一个时间戳,为一个较大的整数。这行代码的目的是将时间戳的低 32 位写入 buf
。不过,当now
的第三十二位(自低向高)为 1 时,now & 0xffffffff
出来的会是负数,这样在writeUint32BE
的检查中就会报错,因为这个函数不允许写入负数。
now
的位数显然超过了 32 比特,如果在一些类型比较强的语言里面,应该会被视为一个64位整数。那么做位运算之后应该还是一个64位整数。上面这个错误说明在Js中,一个64位整型的数字在位运算之后得到的却是一个32位有符号数。是否是0xffffffff
限制了输出的位数呢?
1 | > a = 0xffffffff |
0xffffffff 当做32位有符号数就是-1
可见我们用64位的mask去做位运算得到的还是32位有符号数。
这种乱象的根本原因是,js实际上是使用 IEEE-754 浮点数来表示所有的数字的,包括整型也是用浮点数来表示的。另外,在js中,位运算的输出总是被当做有符号32位数字source。为了摒除这两个因素的影响,我们就无法用位运算来提取低32位的内容,而应该转而使用取余运算:
1 | > > a = 0xffffffff |
其中4294967296 = 0x1000000000
, 4294967295=0xffffffff
。