Сдвиг вправо 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))
Это возвращает правильное значение независимо от того, вставлено оно или нет, но, конечно, обычно вы хотите, чтобы оно было встроено. Также хорошо убедиться, что аргументы на самом деле примитивны int
s, так что внутреннее может сработать.
Вот что он компилирует в 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)))
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…
инструкция.
Закрытое выражение:
#(format "%08x" (clojure.lang.Numbers/unsignedShiftRightInt (unchecked-int 0xf2345678) 4))
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