Когда вы выполняете (-1 >>> 0)
, вы выполняете беззнаковый сдвиг вправо. Беззнаковый здесь ключ. Согласно spe c результат >>>
всегда беззнаковый. -1
представлен как два дополнения из 1
. В двоичном формате это все 1
s (в 8-битной системе это будет 11111111
).
Итак, теперь вы делаете его беззнаковым, выполняя >>> 0
. Вы говорите: «сдвиньте двоичное представление -1
, которое состоит из 1
s, на нулевые биты (не делайте изменений), но заставьте его возвращать беззнаковое число». Итак, вы получаете значение всех 1
s. Go на любую javascript консоль в браузере и набираете:
console.log(2**32 - 1) //4294967295
// 0b means binary representation, and it can have a negative sign
console.log(0b11111111111111111111111111111111) //4294967295
console.log(-0b1 >>> 0) //4294967295
Помните, что 2 **
любое число минус 1
всегда все единиц в двоичном формате. Это то же количество единиц, что и степень, в которую вы подняли два. Итак, 2**32 - 1
равно 32 1
с. Например, два в третьей степени (восемь) минус один (семь) равно 111
в двоичный.
Итак, для следующего (-1 >>> 32) === (2**32 - 1)
.... давайте рассмотрим несколько вещей. Мы знаем, что двоичное представление -1
- это все 1
s. Затем сдвиньте его вправо на один di git, и вы получите то же значение, что и все 1
, но перед ним стоит ноль (и верните беззнаковое число).
console.log(-1 >>> 1) //2147483647
console.log(0b01111111111111111111111111111111) //2147483647
И продолжайте сдвигать, пока не получите 31 ноль и один 1
в конце.
console.log(-1 >>> 31) //1
Это имеет смысл для меня, у нас 31 0
s и один 1
теперь для наших 32 бит.
Итак, вы нажимаете странный случай, сдвиг еще раз должен привести к нулю правильно?
Согласно spe c:
6.1.6.1.11 Number::unsignedRightShift ( x, y )
Let lnum be ! ToInt32(x).
Let rnum be ! ToUint32(y).
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
Return the result of performing a zero-filling right shift of lnum by shiftCount bits. Vacated bits are filled with zero. The result is an unsigned 32-bit integer.
Итак, мы знаем, что у нас уже есть -1
, а это всего 1
комплимент. И мы собираемся сдвинуть его в соответствии с последним шагом документации на shiftCount
бит (который, по нашему мнению, равен 32). И shiftCount
это:
Пусть shiftCount будет результатом маскирования всех, кроме младших 5 битов rnum, то есть вычислить rnum & 0x1F.
Так что же такое rnum & 0x1F
? Ну &
означает поразрядную операцию AND
. lnum
- это число слева от >>>
, а rnum
- это число справа от него. Итак, мы говорим 32 AND 0x1F
. Помните, что 32 - это 100000
. 0x
является шестнадцатеричным, где каждый символ может быть представлен 4
битами. 1
равно 0001
, а F - 1111
. Таким образом, 0x1F
равно 00011111
или 11111
(31
в базе 10, 2**5 - 1
также).
console.log(0x1F) //31 (which is 11111)
32: 100000 &
0x1F: 011111
---------
000000
Количество бит, на которое нужно сдвинуть, если он равен нулю. Это связано с тем, что начальный 1
в 32
не является частью 5
старших битов! Слишком много цифр. Итак, берем 32 1
с и сдвигаем в ноль! Поэтому. Ответ все равно 32 1
с.
В примере -1 >>> 31
это имело смысл, потому что 31 - это <= 5
бит. Итак, мы сделали
31: 11111 &
0x1F: 11111
-------
11111
и сдвинули его 31
бит .... как и ожидалось.
Давайте проверим это дальше .... давайте сделаем
console.log(-1 >>> 33) //2147483647
console.log(-1 >>> 1) //2147483647
Это имеет смысл, просто сдвиньте его на один бит.
33: 100001 &
0x1F: 011111
---------
00001
Итак, go над 5
битами с помощью побитового оператора и запутаетесь. Хотите поиграть в пустышку с человеком, который не исследовал ECMAScript, чтобы ответить на сообщение stackoverflow? Просто спросите, почему они такие же.
console.log(-1 >>> 24033) //2147483647
console.log(-1 >>> 1) //2147483647
Ну конечно, потому что
console.log(0b101110111100001) // 24033
console.log(0b000000000000001) // 1
// ^^^^^ I only care about these bits!!!