Сдвиг вправо 32-битные

Кажется, что все операции сдвига битов в 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,

Каков наиболее эффективный способ реализовать 32-разрядное беззнаковое смещение вправо в Clojure?

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. Clojure deftype:

    (deftype Foo [^int x ^int y]
      clojure.lang.Counted
      (count [this]
        (unsigned-bit-shift-right-int x y)))
    
  2. Bytecode:

    // 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. Закрытое выражение:

    #(format "%08x"
       (clojure.lang.Numbers/unsignedShiftRightInt (unchecked-int 0xf2345678) 4))
    
  2. Bytecode:

    // 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
    
Другие вопросы по тегам