我需要做一个左移操作,其行为方式与JavaScript完全相同。问题是这个:
a << 16
仅当 <= 32767 时,行为类似于 Clojure 的"位移左":
// 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
我注意到这是 two 的补码,我注意到 JS 可能正在这样做,因为它不能(出于某种原因)为此使用超过 32 位(所有在 32 位上完成的位级操作,也许?),所以我想知道如果它高于 32767,我是否应该将 2 的补码应用于数字。但话又说回来,我是 Clojure 的新手,所以我不太确定该怎么做。
首先,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)
在 Clojure (1.8.0) 中计算为 -1841889280
,37431 << 16
在 Node (4.4.5) 中计算为相同的数字,-1841889280
,所以我认为那里没有任何问题。不过,您必须在 JavaScript 中将>>> 0
应用于您的数字,才能在字符串表示中获得预期的位:
// 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
s 和 long
s),因此如果您尝试将int
或long
移动超过 31/63 位,您将无法获得预期的结果。 java.lang.BigInteger
有一个没有此限制的shiftLeft
方法。