右移32位整数

Cac*_*tus 5 clojure bit-shift signedness

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"
Run Code Online (Sandbox Code Playgroud)

但是,这成为负数无符号右移的问题:

user=> (format "%08x" (unchecked-int (unsigned-bit-shift-right (unchecked-int 0xf2345678) 4)))
"ff234567"
Run Code Online (Sandbox Code Playgroud)

当然,正确的答案是0f234567.

在Clojure中实现32位无符号右移的最有效方法是什么?

Mic*_*zyk 5

这可以通过调用int clojure.lang.Numbers.unsignedShiftRightInt(int, int)使用>>>on int参数的方法返回来完成int.它目前在任何地方都没有作为函数公开,但它确实有一个内部实现(相当于>>>Java),你可以直接调用它或包装你自己的inlinable函数:

(defn unsigned-bit-shift-right-int
  {:inline (fn [x n] `(clojure.lang.Numbers/unsignedShiftRightInt ~x ~n))}
  [x n]
  (clojure.lang.Numbers/unsignedShiftRightInt x n))
Run Code Online (Sandbox Code Playgroud)

这会返回正确的值,无论它是否被内联,但当然通常你希望它被内联.确保参数实际上是原始ints以便内在可以启动也是很好的.

这是Clojure 1.8在两种可能情况下编译的内容(非内联案例是常规函数调用,没有什么可看的):

内联原始参数:

滥用count一点只是为了说明这一点.注意iushr说明.

  1. Clojure deftype:

    (deftype Foo [^int x ^int y]
      clojure.lang.Counted
      (count [this]
        (unsigned-bit-shift-right-int x y)))
    
    Run Code Online (Sandbox Code Playgroud)
  2. 字节码:

    // 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
    
    Run Code Online (Sandbox Code Playgroud)

内联非原始参数:

注意invokestatic clojure.lang.Numbers.unsignedShiftRight…说明.

  1. Clojure表达:

    #(format "%08x"
       (clojure.lang.Numbers/unsignedShiftRightInt (unchecked-int 0xf2345678) 4))
    
    Run Code Online (Sandbox Code Playgroud)
  2. 字节码:

    // 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
    
    Run Code Online (Sandbox Code Playgroud)