在 Clojure 中实现 JavaScript 的左移运算符

Implementing JavaScript's left shift operator in Clojure

本文关键字:左移 运算符 JavaScript 实现 Clojure      更新时间:2023-09-26

我需要做一个左移操作,其行为方式与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) 中计算为 -184188928037431 << 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),因此如果您尝试将intlong移动超过 31/63 位,您将无法获得预期的结果。 java.lang.BigInteger有一个没有此限制的shiftLeft方法。