【问题标题】:Right-shifting 32-bit ints右移 32 位整数
【发布时间】:2016-01-21 02:07:01
【问题描述】:

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"

但是,这对于负数的unsigned right-shifting 来说是个问题:

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

当然,正确答案是0f234567

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

【问题讨论】:

  • (format "%08x" (-> 0xf2345678 (unsigned-bit-shift-right 4))) 通过在整个过程中使用 long 为您提供所需的答案。有什么原因你不能在你正在做的事情上使用 long,也许只关注低 32 位?
  • 一种可能性:在移位之前显式屏蔽您的 int,这样它就不会变成负的 64 位 val:(unsigned-bit-shift-right (bit-and (unchecked-int 0xf2345678) 0xffffffff) 4)。 (实际上你在这里可能不需要unchecked-int。)

标签: clojure bit-shift signedness


【解决方案1】:

这可以通过调用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))

无论它是否被内联,这都会返回正确的值,但当然通常你希望它被内联。确保参数实际上是原始的 ints 也很好,这样内在函数就可以发挥作用。

这是它在 Clojure 1.8 中编译成内联的两种可能情况(非内联情况是常规函数调用,在那里什么都看不到):

内联原始参数:

滥用count 只是为了说明这一点。注意iushr 指令。

  1. Clojuredeftype:

    (deftype Foo [^int x ^int y]
      clojure.lang.Counted
      (count [this]
        (unsigned-bit-shift-right-int x y)))
    
  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
    

内联非原始参数:

注意invokestatic clojure.lang.Numbers.unsignedShiftRight… 指令。

  1. Clojure 表达式:

    #(format "%08x"
       (clojure.lang.Numbers/unsignedShiftRightInt (unchecked-int 0xf2345678) 4))
    
  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
    

【讨论】:

  • 看起来不错,但由于某种原因,#(unsigned-bit-shift-right-int % 32) 似乎是身份,而我原本以为它是常量 0 函数。
  • JVM 的无符号右移指令只使用右操作数的最低 5 位。所以,x &gt;&gt;&gt; 32 等价于x &gt;&gt;&gt; 0System.out.println(1 &gt;&gt;&gt; 32) 在Java 中打印1。这在 Java® 语言规范:Java SE 8 版,§15.19 移位运算符,第 563 页中指定。
猜你喜欢
  • 2013-09-18
  • 2021-10-21
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
相关资源
最近更新 更多