コンピュータにおいて、整数はビット(0または1の数字)の列である2進数として表現される。概念的には、ビット列は左側が無限に続き、最上位ビットはすべて0またはすべて1となる。ビット演算は、このような列の個々のビットに対して行われる。例えば、シフトは、列全体を左または右に1つ以上の位置分移動させ、同じパターンをずらして再現する。
Emacs Lispのビット演算は整数にのみ適用されます。
ash (算術シフト) は、integer のビットを左に count 桁分、あるいは count
が負の場合は右にシフトします。左シフトでは右側にゼロビットが挿入され、右シフトでは最右端のビットが破棄されます。整数演算として考えると、ash は integer に 2**count を乗算し、その結果を負の無限大方向へ切り捨てることで整数に変換します。
以下に、ビットパターンを左右に1ビットずつシフトさせたashの例を示します。これらの例では2進パターンの下位ビットのみを表示しており、先頭のビットはすべて表示されている最上位ビットと一致しています。ご覧の通り、左に1ビットシフトすることは2倍することと同等であり、一方で右に1ビットシフトすることは、2で除算した後に負の無限大方向に丸めることと同等です。
(ash 7 1) ⇒ 14
;; Decimal 7 becomes decimal 14.
...000111
⇒
...001110
(ash 7 -1) ⇒ 3
...000111
⇒
...000011
(ash -7 1) ⇒ -14
...111001
⇒
...110010
(ash -7 -1) ⇒ -4
...111001
⇒
...111100
2ビット左または右にシフトする例を以下に示します。
; binary values (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(論理シフトの略)は、integerのビットを左にcount桁分、あるいはcountが負の場合は右にシフトし、空いたビットに0を補います。countが負の場合、integerは固定小数点数または正のビッグナンバーである必要があり、lshはシフト前にmost-negative-fixnumを2回減算することで、負の固定小数点数をあたかも符号なしであるかのように扱い、非負の結果を導き出します。この特異な挙動は、Emacsが固定小数点数のみをサポートしていた時代にまで遡ります。現在ではashの方が適切な選択肢です。
lsh は、integer と count がともに負である場合を除き ash
と同様に動作するため、以下の例ではこれらの例外的なケースに焦点を当てます。 これらの例では、30ビットの固定小数点数を想定しています。
; binary values (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となります。
例えば、4ビットの2進数を用いると、13と12のビット単位のAND演算の結果は12となります。1101と1100をAND演算すると1100が得られます。どちらの2進数においても、左端の2ビットはともに1であるため、戻り値の左端の2ビットもともに1となります。一方、右端の2ビットについては、少なくとも一方の引数でそれぞれが0となっているため、戻り値の右端の2ビットはともに0となります。
したがって、
(logand 13 12)
⇒ 12
logand に引数が渡されない場合、−1
を返します。この数値の2進表現はすべて1で構成されているため、logand
の単位元となります。logand に引数が1つだけ渡された場合は、その引数を返します。
; binary values (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
この関数は、引数のビット単位の包含論理和を返します。結果の n 番目のビットが 1
となるのは、少なくとも一方の引数において n 番目のビットが 1
である場合、かつその場合に限ります。引数がない場合は結果は 0 となり、これはこの演算における単位元となります。logior に引数が 1 つだけ渡された場合は、その引数をそのまま返します。
; binary values (logior 12 5) ; 12 = ...001100 ; 5 = ...000101 ⇒ 13 ; 13 = ...001101
(logior 12 5 7) ; 12 = ...001100 ; 5 = ...000101 ; 7 = ...000111 ⇒ 15 ; 15 = ...001111
この関数は、引数のビット単位の排他的論理和を返します。結果の n 番目のビットが 1
となるのは、引数のうち n 番目のビットが 1
であるものが奇数個存在する場合、かつその場合に限ります。引数がない場合は結果は 0 となり、これはこの演算における単位元となります。logxor に引数が 1 つだけ渡された場合は、その引数をそのまま返します。
; binary values (logxor 12 5) ; 12 = ...001100 ; 5 = ...000101 ⇒ 9 ; 9 = ...001001
(logxor 12 5 7) ; 12 = ...001100 ; 5 = ...000101 ; 7 = ...000111 ⇒ 14 ; 14 = ...001110
この関数は、引数のビット単位の補数を返します。結果の n 番目のビットが 1 となるのは、integer の n 番目のビットが 0 であるとき、かつそのときに限ります。逆も同様です。結果は −1 − integer となります。
(lognot 5)
⇒ -6
;; 5 = ...000101
;; becomes
;; -6 = ...111010
この関数は、integerのハミング重み、すなわちintegerの2進表現に含まれる1の数を返します。integerが負の場合、その2の補数表現における0ビットの数を返します。結果は常に非負となります。
(logcount 43) ; 43 = ...000101011 ⇒ 4 (logcount -43) ; -43 = ...111010101 ⇒ 3