Clojure的位移运算似乎都返回64位long
结果,即使对于32位int
参数也是如此。这对bit-shift-left
来说不是一个实质性的问题:
user=> (format "%08x" (unchecked-int (bit-shift-left (unchecked-int 0x12345678) 4)))
"23456780"
user=> (format "%08x" (unchecked-int (bit-shift-left (unchecked-int 0xf2345678) 4)))
"23456780"
然而,这成为负数的无符号右移的问题:
user=> (format "%08x" (unchecked-int (unsigned-bit-shift-right (unchecked-int 0xf2345678) 4)))
"ff234567"
当然,正确答案是 0f234567
.
在 Clojure 中实现 32 位无符号右移的最有效方法是什么?
可以通过调用int clojure.lang.Numbers.unsignedShiftRightInt(int, int)
方法来实现,该方法在int
参数上使用>>>
,返回int
。它目前没有在任何地方公开为函数,但它确实有一个内部实现(相当于 Java 中的>>>
),你可以直接调用它,也可以包装在你自己的内联函数中:
(defn unsigned-bit-shift-right-int
{:inline (fn [x n] `(clojure.lang.Numbers/unsignedShiftRightInt ~x ~n))}
[x n]
(clojure.lang.Numbers/unsignedShiftRightInt x n))
无论它是否内联,这都会返回正确的值,但当然,通常您希望它被内联。确保参数实际上是原始的int
也是很好的,以便内在的可以启动。
以下是它在 Clojure 1.8 中编译的内容,在两种可能的情况下,它确实被内联了(非内联情况是常规函数调用,那里没有什么可看的):
内联原始参数:
稍微滥用count
一点只是为了说明这一点。请注意iushr
说明。
Clojure
deftype
:(deftype Foo [^int x ^int y] clojure.lang.Counted (count [this] (unsigned-bit-shift-right-int x y)))
字节码:
// Method descriptor #61 ()I // Stack: 2, Locals: 1 public int count(); 0 aload_0 [this] 1 getfield user.Foo.x : int [19] 4 aload_0 [this] 5 getfield user.Foo.y : int [21] 8 iushr 9 ireturn Line numbers: [pc: 0, line: 1] [pc: 8, line: 4] Local variable table: [pc: 0, pc: 9] local: this index: 0 type: user.Foo
内联非原始参数:
请注意invokestatic clojure.lang.Numbers.unsignedShiftRight…
说明。
克洛居尔表达式:
#(format "%08x" (clojure.lang.Numbers/unsignedShiftRightInt (unchecked-int 0xf2345678) 4))
字节码:
// Method descriptor #11 ()Ljava/lang/Object; // Stack: 5, Locals: 1 public java.lang.Object invoke(); 0 getstatic user$eval16141$fn__16142.const__0 : clojure.lang.Var [15] 3 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [20] 6 checkcast clojure.lang.IFn [22] 9 ldc <String "%08x"> [24] 11 ldc2_w <Long 4063516280> [25] 14 l2i 15 ldc2_w <Long 4> [27] 18 invokestatic clojure.lang.RT.intCast(long) : int [34] 21 invokestatic clojure.lang.Numbers.unsignedShiftRightInt(int, int) : int [40] 24 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [46] 27 invokeinterface clojure.lang.IFn.invoke(java.lang.Object, java.lang.Object) : java.lang.Object [49] [nargs: 3] 32 areturn Line numbers: [pc: 0, line: 1] [pc: 6, line: 1] [pc: 14, line: 1] [pc: 21, line: 1] [pc: 27, line: 1] Local variable table: [pc: 0, pc: 32] local: this index: 0 type: java.lang.Object