Реализация оператора левого смещения JavaScript в Clojure
Мне нужно сделать операцию левого сдвига, которая ведет себя точно так же, как JavaScript. Проблема в том, что это:
a << 16
ведет себя как Clojure "bit-shift-left", только если a <= 32767:
// JS
32767 << 16 // 2147418112
32768 << 16 // -2147483648
567890 << 16 // -1437466624
;; CLJ
(bit-shift-left 32767 16) // 2147418112
(bit-shift-left 32768 16) // 2147483648
(bit-shift-left 567890 16) // 37217239040
Я заметил, что при выполнении "37431 << 16" JS делает что-то совершенно отличное от Clojure на двоичном уровне. В то время как Clojure преобразует 1001001000110111 в 10010010001101110000000000000000, JS преобразует 1001001000110111 в 1101101110010010000000000000000:
// CLJ, then JS
10 01001 00011 01110 00000 00000 00000
1 10110 11100 10010 00000 00000 00000
Я заметил, что это дополнение к двум, и я заметил, что JS может делать это, потому что он не может (по какой-то причине) использовать более 32 бит для этого (все операции на уровне битов, выполняемые на 32 битах, может быть?), Поэтому мне интересно, если Я должен применить два числа к числу, если оно выше 32767. Но опять же, я новичок Clojure, поэтому я не очень уверен, как это сделать.
1 ответ
Во-первых, clojure.core/bit-shift-left
будет рассматривать свой левый вход как long
, Ты можешь использовать clojure.lang.Numbers/shiftLeftInt
сдвинуть число как int
:
(clojure.lang.Numbers/shiftLeftInt 567890 16)
;= -1437466624
Это соответствует результату, который вы получаете в JavaScript. Там нет обертки вокруг этого статического метода в clojure.core
, но вы можете предоставить свой собственный.
Во-вторых, (clojure.lang.Numbers/shiftLeftInt 37431 16)
оценивает -1841889280
в Clojure (1.8.0) и 37431 << 16
оценивает к тому же числу, -1841889280
в Node (4.4.5), так что я не думаю, что там есть какие-либо проблемы. Вам придется подать заявку >>> 0
на ваш номер в JavaScript, чтобы получить ожидаемые биты в строковом представлении, хотя:
// Node 4.4.5
> ((37431 << 16) >>> 0).toString(2)
'10010010001101110000000000000000'
Хорошо отметить, что вылов отдельных кусочков &
прекрасно работает без >>> 0
"неподписанный актерский состав":
> (37431 << 16) & (1 << 31)
-2147483648
> (37431 << 16) & (1 << 30)
0
> (37431 << 16) & (1 << 29)
0
> (37431 << 16) & (1 << 28)
268435456
И вы можете вычислить оба строковых представления в Clojure:
(Integer/toString (clojure.lang.Numbers/shiftLeftInt 37431 16) 2)
;= "-1101101110010010000000000000000"
(Integer/toBinaryString (clojure.lang.Numbers/shiftLeftInt 37431 16))
;= "10010010001101110000000000000000"
Обратите внимание, что в Java операторы битового сдвига берут только самые правые 5 или 6 бит (для int
с и long
s, соответственно) правого операнда, так что если вы попытаетесь сместить int
или же long
более чем на 31/63 бита вы не получите ожидаемого результата. java.lang.BigInteger
имеет shiftLeft
метод, который не имеет этого ограничения.