我正在实现BER压缩整数的解码,最近我发现了与大整数的按位运算相关的奇怪的JavaScript行为。
E.g:
var a = 17516032; // has 25 bits
alert(a << 7) // outputs -2052915200
alert(a * 128) // outputs 2242052096
alert(2242052096 >> 16) // outputs -31325
alert(2242052096 / 65536) // outputs 34211
虽然第一种解决方法(乘法而不是左移)是可以接受的,但第二种不是。
为什么会这样?怎么忍受呢?
答案 0 :(得分:7)
17516032
为00000001000010110100011000000000
。向左移动7可以得到10000101101000110000000000000000
。这等于two's complement中的-2052915200
(几乎所有计算机都代表负数)。
>>
是签名的右移。这意味着最左边的位(确定一个数字的符号)将被移到左侧。
e.g。
1100 >> 2 == 1111
0111 >> 2 == 0001
如果你想做一个无符号移位(忽略符号位),使用>>>
将零填充位串的左端。
答案 1 :(得分:3)
按位运算符用于32位整数,而乘法和除法用于浮点数。
当移位数字时,它在操作之前从浮点数转换为32位整数,并在操作之后转换回浮点数。数字2242052096设置了第32位,因此在转换为32位整数时为负数。
>>
右移运算符不会更改值的符号,即从左侧移入的位具有与符号位相同的值。使用>>>
右移运算符来改为零位。
答案 2 :(得分:2)
(2242052096 / 65536) == (2242052096 >>> 16)
注意不同的转变。
答案 3 :(得分:1)
Javascript通常将数字表示为(双精度)浮点数。
几乎所有按位运算都转换为带符号的32位整数,执行它们要做的任何事情,然后在转换回来时将结果视为带符号的32位整数。
异常是>>>
,它在转换回来时将结果视为无符号 32位整数。
所以:
>>>
代替>>
; a * 128
给出了预期的答案,因为它从未首先转换为带符号的32位整数 - 它只是一个浮点乘法; a << 7
给出了意想不到的答案,因为它被转换为带符号的32位整数,然后将1
移入符号位,从而产生负的带符号32位值。没有<<<
,但如果您想将左移作为班次,可以使用
(a << 7) >>> 0
获得预期的答案(>>> 0
有效地将带符号的32位值转换为无符号的32位值。)