コンピューターの中では、整数はビット(bit: 0か1の数字)のシーケンスである2進数で表されます。ビットシーケンスは概念的には最上位ビットがすべて0か1であるような左側に無限なシーケンスです。ビット演算はそのようなシーケンスの中の個々のビットに作用します。たとえばシフト(shifting)はシーケンス全体を1つ以上左または右に移動して、移動されたのと同じパターンを再現します。
Emacs Lispのビット演算は整数だけに適用されます。
ash
(算術シフト(arithmetic
shift))は、integer1の中のビット位置を左にcountシフトする。countが負なら右にシフトする。左シフトでは右側に0が挿入されて、右シフトでは最右ビットが破棄される。整数処理として考えると、ash
はinteger1に
2**count
を乗じてから、負の無限大に向かって丸めることによりその結果を変換する。
以下はビットパターンを1ビット左にシフトしてから右にシフトする例。この例で2進数パターンの下位ビットだけを示している。先頭ビットはすべて表示されている最上位ビットと一致する。ご覧のとおり1ビットの左シフトは2を乗ずること、1ビットの右シフトは2で除してから負の無限大方向に丸められることと等価である。
(ash 7 1) ⇒ 14
;; 10進の7は10進の14になる
...000111
⇒
...001110
(ash 7 -1) ⇒ 3 ...000111 ⇒ ...000011
(ash -7 1) ⇒ -14 ...111001 ⇒ ...110010
(ash -7 -1) ⇒ -4 ...111001 ⇒ ...111100
以下は2ビット左にシフトしてから右に2ビットシフトする例:
; 2進数値 (ash 5 2) ; 5 = ...000101 ⇒ 20 ; = ...010100 (ash -5 2) ; -5 = ...111011 ⇒ -20 ; = ...101100
(ash 5 -2)
⇒ 1 ; = ...000001
(ash -5 -2)
⇒ -2 ; = ...111110
lsh
はlogical
shiftの略で、integer1のビットを左にcount回シフト(countが負なら右にシフト、空いたビットには0を補填)する。countが負ならinteger1はfixnumか正のbignumのいずれかでなければならず、lsh
はシフト前に負のfixnumをmost-negative-fixnum
で2回減算してあたかも符号なしであるかのように非負の結果を生成する。この奇妙な振る舞いはEmacsがfixnumsだけをサポートしていた頃の振る舞いであり、現在ではash
がより良い選択である。
integer1とcount1がいずれも負の場合を除いてlsh
はash
のように振る舞うので、以下の例ではこれらの例外ケースに焦点をあてている。これらの例は30ビットのfixnumsを想定している。
; 2進数値 (ash -7 -1) ; -7 = ...111111111111111111111111111001 ⇒ -4 ; = ...111111111111111111111111111100 (lsh -7 -1) ⇒ 536870908 ; = ...011111111111111111111111111100
(ash -5 -2) ; -5 = ...111111111111111111111111111011 ⇒ -2 ; = ...111111111111111111111111111110 (lsh -5 -2) ⇒ 268435454 ; = ...001111111111111111111111111110
この関数は引数のビットのANDをリターンする。すべての引数のn番目のビットが1の場合に限り、結果のn番目のビットが1となる。
たとえば13と12では、4ビット2進数を使用すると1101と1100のビットANDは1100を生成する。この2進数ではいずれも左の2ビットがセット(つまり1)されているので、リターンされる値の左2ビットがセットされる。しかし右の2ビットにたいしては少なくとも1つの引数でそのビットが0なので、リターンされる値の右2ビットは0になる。
したがって、
(logand 13 12) ⇒ 12
logand
に何も引数も渡さなければ、値−1がリターンされる。−1を2進数で表すとすべてのビットが1なので、−1はlogand
にたいする単位元(identity
element)である。logand
に渡す引数が1つだけならその引数がリターンされる。
; 2進数値 (logand 14 13) ; 14 = ...001110 ; 13 = ...001101 ⇒ 12 ; 12 = ...001100
(logand 14 13 4) ; 14 = ...001110 ; 13 = ...001101 ; 4 = ...000100 ⇒ 4 ; 4 = ...000100
(logand)
⇒ -1 ; -1 = ...111111
この関数は、引数のビット単位の包含的ORをリターンする。少なくとも1つの引数でn番目のビットが1なら、結果のn番目のビットが1になる。引数を与えなければ、結果はこの処理にたいする単位元である0となる。logior
に渡す引数が1つだけならその引数がリターンされる。
; 2進数値 (logior 12 5) ; 12 = ...001100 ; 5 = ...000101 ⇒ 13 ; 13 = ...001101
(logior 12 5 7) ; 12 = ...001100 ; 5 = ...000101 ; 7 = ...000111 ⇒ 15 ; 15 = ...001111
この関数は、引数のビット単位の排他的ORをリターンする。n番目のビットが1であるような引数の数が奇数個の場合のみ、結果のn番目のビットが1となる。引数を与えなければ、結果はこの処理の単位元である0となる。logxor
に渡す引数が1つだけならその引数がリターンされる。
; 2進数値 (logxor 12 5) ; 12 = ...001100 ; 5 = ...000101 ⇒ 9 ; 9 = ...001001
(logxor 12 5 7) ; 12 = ...001100 ; 5 = ...000101 ; 7 = ...000111 ⇒ 14 ; 14 = ...001110
この関数は引数のビット単位の補数(bitwise complement)をリターンする。integerのn番目のビットが0の場合に限り、結果のn番目のビットが1になり、その逆も成り立つ。結果は−1 − integerと等価。
(lognot 5) ⇒ -6 ;; 5 = ...000101 ;; becomes ;; -6 = ...111010
この関数はintegerのハミング重み (Hamming weight: integerの2進数表現での1の個数)をリターンする。integerが負なら、その2の補数の2進数表現での0ビットの個数をリターンする。結果は常に非負となる。
(logcount 43) ; 43 = ...000101011 ⇒ 4 (logcount -43) ; -43 = ...111010101 ⇒ 3