ビット(bit)とは、2進数の1桁のことである。0か1の2つのうちどちらかの値を取る。ビットと命名したのは情報理論で有名なシャノンであるらしい。コンピュータは、内部では2進数で動いていることは有名だが、この2進数の桁そのものに注目した演算操作がある。Forthで定義されているそのようなワードについて、このページでは説明する。

論理演算


プログラムでは、この1桁1桁を標識として使うことがかなりある。フラグ(旗)などという。1だとフラグが立っているといい、0だと立っていないのである。あるいは、1ならオン(on)、0ならオフ(off)である。

この1,0を真理値と捉えて、ビット毎に論理計算をするのが論理演算である。1が真、0が偽である。1セル分の全ビット一斉に行う。32ビットシステムなら32桁分、64ビットシステムなら64桁分、同位置の各ビットについて一回に行うのである。
AND   ( n n'  -- n'' )   \ "かつ"。両入力の桁毎に、両方1なら1、それ以外は0。
OR    ( n n' -- n'' )    \ "または"。両入力の桁毎に、その一方または両方が1なら1。両方0のときだけ0。
XOR   ( n n' -- n'' )    \ 排他的"または"。どちらか一方だけが1のとき1。両方が1のときと両方が0のときは0。
NOT   ( n -- n' )        \ 各ビットについて、0を1に、1を0に反転。
INVERT ( n -- n' )    \ NOTと同じ
Forthでは、1セルの真理値が、TRUE および FALSE の定数として定義されている。これらは、数値としては、-1と0に設定されている。-1が真(TRUE)、0が偽(FALSE)である。-1はコンピュータ内の数値表現として、全ビット1として定義されているので、この真理値に論理演算を操作すれば、結果が命題論理の真理値と一致する。
TRUE FALSE AND  \ -- false
TRUE FALSE OR   \ -- true
TRUE TRUE XOR   \ -- false
FALSE NOT       \ -- true
しかし、上の論理演算は、セルに対する論理演算ではなく、ビット毎の演算であると考えた方が良い。実際の用法も、上で触れた特定のビットにフラグを設定したりチェックしたりするための使用が多い。

ビットシフト


1セル数のビットをスライドさせるワードもある。このスライドまたはシフトは、全ての桁を論理的に右または左に移動させる。
コンピュータ内の数値のビット表現は、数字で考えた場合の桁と物理的にも同じ順で並んでいるとは限らない。"論理的"(logical)にというのは、
それを数字で考えた場合の桁に合わせて、という意味である。実際、x86はリトル・エンディアンといって、バイト(8桁)単位では小さい方の桁から左詰めとなっており、
他方で、バイトの中身のビットは小さい方の桁から右詰めのようである。すると、ここでの演算は、物理的にはビットを直線的に移動させるものではなく、
バイト内を巡回しながら移動させるものとなる。そのせいか、ビットシフトは他の演算と比べて少し遅いらしい。

Forth標準で規定されているものは次の通りである:
LSHIFT   ( n uc -- n' )  \ 数値nのビットの全体をuc桁左にずらす。空いた右の桁は0をつめる。左は2進数の大きい方の桁を意味し、1 LSHIFTは2倍と同じ。
RSHIFT   ( n uc -- n' )  \ 数値nのビットの全体をuc桁右にずらす。空いた左の桁は0をつめる。1 RSHFTは正の数なら2分の1(余りは捨てる)を意味する。
ARSHIFT  ( n uc -- n' )  \ 算術的右シフト。RSHIFTと同じだが、nの左端のビットが1か0かに応じて、空いた左の桁に1か0を詰める。
シフトの数は、0以上で、かつ、1セルのビット数未満でなければならない。iMopsの場合、下の6ビット分(0から63まで)の数字がシフト量として採られ、それを越える桁は無視される(機械の仕様)。
(論理的に)左端のビットは、符号ありの数値表現であれば、そのビットが1であることが負の数であることを示す。ARSHIFTは、したがって、符号を維持するのである。別のページで触れたように、割り算は演算として非常に遅いので、2の累乗の数での割り算は、このARSHIFTを用いて実現すれば、計算速度のかなりの高速化が期待できる。

それぞれの同義語として、次のワードが定義されていることも多い。Mopsではこちらが主である。
<<   ( n uc -- n' )  \ LSHIFT
>>   ( n uc -- n' )  \ RSHIFT
A>>  ( n uc -- n' )  \ ARSHIFT
簡単な実施例:
1 4 <<    \ -- 16
32 2 >>   \ -- 8
-32 2 A>>  \ -- -8

数値比較判定


数値の大小関係を比較するワードは、次の通りである。これらは、結果に応じて、TUREまたはFALSEのフラグをスタックに残す。
=    ( n1 n2 -- b )  \ n1とn2が等しいときTRUE、等しくないときはFALSEを残す。
<>   ( n1 n2 -- b )  \ n1とn2が相異なるときTRUE、等しいときはFALSEを残す。
<    ( n1 n2 -- b )  \ n1がn2より小さいときTRUE、それ以外はFALSEを残す。
>    ( n1 n2 -- b )  \ n1がn2より大きいときTRUE、それ以外はFALSEを返す。
<=   ( n1 n2 -- b )  \ n1がn2より小さいか等しいときTRUE、それ以外はFALSE
>=   ( n1 n2 -- b )  \ n1がn2より大きいか等しいときTRUE、それ以外はFALSE
0=   ( n -- b )      \ nが0であるときTRUE、それ以外はFALSE
0<>  ( n -- b )      \ nが0でないときTRUE、0のときFALSE
0<   ( n -- b )      \ nが負の数であるときTRUE、それ以外はFALSE
0>   ( n -- b )      \ nが正の数であるときTRUE、それ以外はFALSE
0<=  ( n -- b )      \ nが0か負の数であるときTRUE、正の数のときFALSE
0>=  ( n -- b )      \ nが正の数か0であるときTRUE、負の数のときFALSE
これらは、当然ながら、if-文などの条件分岐の場合に用いられることが多く、また、上の論理演算子と組み合わされることも多い。
0 VALUE VL1
0 VALUE VL2
...

: ....  VL1 0> VL2 0<= AND IF ...
Forthのif-文は、上のように、条件式をifの前におく。スタックに残った値に応じて条件分岐するのである。if-文については、詳しくは、フロー制御のところで説明する。

組み合わせ例(あまり意味はない)


ビット操作は、定数を用いてフラグビットを設定したり、チェックしたりする場合の使用例が多い。if-条件分岐は0でない値は真として扱うので、通常はそのままでよいが、0<>を使うと、正確に真偽がでる。
1 CONSTANT Onion
1 1 << CONSTANT Potato
1 2 << CONSTANT Carrot
Onion Potato or Carrot or CONSTANT Vegetables
1 3 << Constant Chicken
1 4 << CONSTANT Poke
1 5 << CONSTANT Beef
1 8 << CONSTANT Curry
0 VALUE Chicken-Curry
0 VALUE Beef-Curry
0 VALUE Poke-Curry
  Onion Chicken or Curry or -> Chicken-curry
  Onion Potato or Beef or Curry or -> Beef-Curry
  Vegetables Poke or Curry or -> Poke-Curry

  Chicken-Curry Carrot AND 0<> \ false
  Chicken-Curry Potato or -> Chicken-Curry \ チキンカレーにもジャガイモを。
  Poke-Curry Carrot XOR -> Poke-Curry \ Carrotを切り替える。もとがオンならオフ、オフならオン。人参なしの豚肉カレー。
  Beef-Curry Curry XOR Carrot OR  \ -- 肉じゃが的な何かのフラグ




最終更新:2019年02月01日 22:31