Реализация оператора левого смещения 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с и longs, соответственно) правого операнда, так что если вы попытаетесь сместить int или же long более чем на 31/63 бита вы не получите ожидаемого результата. java.lang.BigInteger имеет shiftLeft метод, который не имеет этого ограничения.

Другие вопросы по тегам