HOME > 技術談話 > 論理演算小技1

はじめに 任意のビットをクリア 任意のビットをセット 任意のビットを反転 0,1,2,3の繰り返し 1、2の繰り返し
変数クリア 2の補数 奇数偶数判定 4の倍数判定


■はじめに

 論理演算では算術演算と異なり「ビットの変化」を正しく理解していることが必要です
 普段の生活では利用頻度が少ない演算手法ですが、算術演算や条件判定を高速、最適化することができます。
 ビット操作をサポートしている言語もありますが、本章では汎用的なバイトデータでの利用手法を説明しています。


■任意のビットをクリア

 残すビットには、クリアするビットにはを組み合わせた値で論理積(AND)を行います。

 例えば、あるバイトデータ。内容は5CHの下位4ビットをクリアするのであれば、F0Hで論理積を行います。

0 1 0 1 1 1 0 0 … 5CH
1 1 1 1 0 0 0 0 … F0H
---------------
0 1 0 1 0 0 0 0 … 50H = 5CH & F0H

 左側の8個の数字の並びは2進数表現です。数字の後ろにHが付与されている箇所は16進数表現になります。

 元の値が何であろうとで論理積された結果には元の値が引き継がれていることに注意してください。
 論理演算では同位置のビットに影響を与えますが、その影響範囲は当該ビット位置だけです。
 また、バイトデータは8ビットですが、ロング型のデータを利用すれば32ビットを一度に論理演算することができます。


■任意のビットをセット

 残すビットには、セットするビットにはを組み合わせた値で論理和(OR)を行います。

 例えば、あるバイトデータ。内容は5CHの下位4ビットをセットするのであれは0FHで論理和します。

0 1 0 1 1 1 0 0 … 5CH
0 0 0 0 1 1 1 1 … 0FH
---------------
0 1 0 1 1 1 1 1 … 5FH = 5CH | 0FH

 元の値が何であろうとで論理和された結果には元の値が引き継がれています。


■任意のビットを反転

 残すビットには0、反転するビットには1を組み合わせた値で排他的論理和(XOR)を行います。

 例えば、あるバイトデータ。内容は5CHの上位4ビットを反転するのであればF0Hで排他的論理和します。

0 1 0 1 1 1 0 0 … 5CH
1 1 1 1 0 0 0 0 … F0H
---------------
1 0 1 0 1 1 0 0 … 5FH = 5CH ^ F0H

 元の値が何であろうとで排他的論理和された結果には元の値が引き継がれています。


■0、1、2、3の繰り返し

 0、1、2、3が下位2ビットの連続変化した値であることに気が付けば対応は簡単です。

0 0 0 0 0 0 0 0 … 0
0 0 0 0 0 0 0 1 … 1
0 0 0 0 0 0
1 0 … 2
0 0 0 0 0 0
1 1 … 3

 ここでは最大の値である3を例に説明します。
 まずは元の値に対して+1します。計算上は下位2ビット目からの桁上がりが生じます。

0 0 0 0 0 0 1 1 … 3
0 0 0 0 0 0 0 1 … 1
-----------------
0 0 0 0 0
1 0 0 … 4 = 3 + 1

 この+1した値に下位2ビットを1とした論理積(AND)を行います。

0 0 0 0 0 1 0 0 … 4
0 0 0 0 0 0 1 1 … 03H
-----------------
0 0 0 0 0 0 0 0 … 0 = 4 & 03H

 今回の例では3を元に一連の手順から0を得ることができました。
 元の値が0、1、2の場合も同様です。検算は各自で行ってください。

 さて、この手続きをプログラム風(C言語風であって、C言語ではありません)に記述すると、
  Y = (A + 1) & 03H
となります。非常にシンプルな式です。3から0への回り込み判定のための命令は不要です。

 更に、今回の例では0〜3の4通りの組み合わせですから、この4をTOTALと宣言すれば、
  Y = (A + 1) & (TOTAL - 1)

となります。
 組み合わせ数は事前に定義されていることが多いので、その定義を引用すれば情報を一元管理できます。

 4という値にピンときましたか?
 この方法は
0からの連続した値で組み合わせ数が2の階乗の場合に有効です。例えば、8や16の場合も通用します。

 今回の場合、値の循環する範囲が0〜3で限定されますので、0〜4という指定には利用できません。
 つまり、仕様変更が生じた場合は融通が効かないのが難点です。


■1、2の繰り返し

 この場合は厄介ですが、排他的論理和(XOR)を利用することで実現できます。

 まず、条件を整理します。下位2ビットだけが変化し、ビットが反転していることに気が付くと思います。

0 0 0 0 0 0 0 1 … 1
0 0 0 0 0 0 1 0 … 2

 各ビットが反転していることから、下位2ビットに1で排他的論理和(XOR)を行うと求める値を得ることができます。
 排他的論理和(XOR)は1を指定すると元の値に対して反転する働きがあることを思い出してください。
 では、1の状態から2への移り変わりを見てみます。

0 0 0 0 0 0 0 1 … 1
0 0 0 0 0 0 1 1 … 03H
-----------------
0 0 0 0 0 0 1 0 … 2 = 1 ^ 03H

 続いて2の状態から1への移り変わりを見てみます。

0 0 0 0 0 0 1 0 … 2
0 0 0 0 0 0 1 1 … 03H
-----------------
0 0 0 0 0 0 0 1 … 1 = 2 ^ 03H

 今回の場合は値の循環する範囲が1〜2ですが、それ以外の巡回範囲の場合は対応することが困難です。
 前回のテーマと同様ですが、論理演算は高速化には有利ですが、仕様変更に対する融通が効きません。


■変数クリア

 今時のCPUではクリアのための専用命令がありますが、無い場合の対処方法です。
 多くの場合、算術演算で00Hを代入する方法が採用されますが、この00Hを容易に生成する方法を紹介します。

 方法は単純です。値は何でも良く、値同士を排他的論理和(XOR)するだけです。
 例えば、既に存在するレジスタの値が5CHの場合、当該レジスタを当該レジスタで排他的論理和します。

0 1 0 1 1 1 0 0 … 5CH
0 1 0 1 1 1 0 0 … 5CH
---------------
0 0 0 0 0 0 0 0 … 00H = 5CH ^ 5CH

 排他的論理和では入力値が同じ場合は0になるという特性を利用しています。
 C言語で記述した場合は想定外の結果(アセンブラ内容)が得られる場合があるので注意してください。
 この方法はプログラマがアセンブラで命令を記述する場合に明示的な効果があります。


■2の補数

 2の補数は算術演算を利用し、0から値を引き算することで作成することができます。表現としては、
  Y = 0 - A 、 もしくは、 Y = - A
です。何れの場合でも事前に00Hが必要になることは確かです。

 では論理演算を使用して2の補数を得る場合はどうするか?
 ずばり、2の補数を作成するには
値を反転して+1です。筆算でも簡単にできますので覚えておいてください。

 では、+5の2の補数を作成し-5にする例を考えます。
 まず、元の値を否定(NOT)します。

0 0 0 0 0 1 0 1 … 5
-----------------
1 1 1 1 1 0 1 0 … FAH = ~05H

 つづいて+1を行います。

1 1 1 1 1 0 1 0 … FAH
0 0 0 0 0 0 0 1 … 01H
-----------------
1 1 1 1 1 0 1 1 … FBH = FAH + 1 = -5

 値を反転しただけの場合を1の補数と呼びますが、1の補数は以後の算術演算で直接利用することができません。
 2の補数表現された値だけが正の値との混合演算が可能です。

 蛇足ですが、-5の2の補数を作成すると+5にになります。これについては各自で検算してください。


■奇数偶数判定

 任意の値が奇数であるか偶数であるかを判断したい場合があります。
 この場合、2で割った時の余りの有無で判断する方法があります。が、割り算は比較的重い処理になってしまいます。

 コンパイラによっては効率よく判断してくれる場合もありますが、ここではその効率の良い方法について説明します。

 まず奇数と偶数の特性を整理します。単純に2で割り切れる値が偶数です。まさに算数の世界です。
 0〜5までの数を2進数表現と共に以下にまとめますが、偶数と奇数はそれぞれ1つおきに出現しますね?

0 0 0 0 0 0 0 0 … 0  0 / 2 = 0 ... 0
0 0 0 0 0 0 0 1 … 1  1 / 2 = 0 ... 1
0 0 0 0 0 0 1 0 … 2  2 / 2 = 1 ... 0
0 0 0 0 0 0 1 1 … 3  3 / 2 = 1 ... 1
0 0 0 0 0 1 0 0 … 4  4 / 2 = 2 ... 0
0 0 0 0 0 1 0 1 … 5  5 / 2 = 2 ... 1

 これにヒントの色を加えます。

0 0 0 0 0 0 0 0 … 0  0 / 2 = 0 ... 0
0 0 0 0 0 0 0 1 … 1  1 / 2 = 0 ... 1
0 0 0 0 0 0 1 0 … 2  2 / 2 = 1 ... 0
0 0 0 0 0 0 1 1 … 3  3 / 2 = 1 ... 1
0 0 0 0 0 1 0
0 … 4  4 / 2 = 2 ... 0
0 0 0 0 0 1 0 1 … 5  5 / 2 = 2 ... 1

 気づきましたか?
 そうです。最下位のビットが0、1、0、1と繰り返すと共に、0の時に偶数、1の時に奇数であることが判明します。

 つまり、最下位ビットだけ注目すれば奇数と偶数は判断できるのです。
 すなわち最下位ビットだけ
1論理積(AND)を行えば、その結果だけで奇数と偶数を判断することができます。

 試しに、偶数の代表として4を演算してみます。

0 0 0 0 0 1 0 0 … 04H
0 0 0 0 0 0 0 1 … 01H
---------------
0 0 0 0 0 0 0 0 … 00H = 04H & 01H

 結果は0になりました。つまり、偶数であることが判明します。
 つづいて、奇数の代表として5を演算してみます。

0 0 0 0 0 1 0 1 … 05H
0 0 0 0 0 0 0 1 … 01H
---------------
0 0 0 0 0 0 0 1 … 01H = 05H & 01H

 今度は結果が1になりました。よって奇数であることが判明します。

 01Hで論理積(AND)することは2で割った余りを求めた場合と同じ結果を得ることができます。


■4の倍数判定

 任意の値が4の倍数であるかを判断したい場合があります。
 この場合、前述の奇数偶数判定と同様、4で割った余りが0であれば4の倍数と判断することができます。が、…。
 ここでも割り算を使わずに高速に判断する方法について考えてみます。

 奇数偶数判定と同様、値を確認してみます。確かに4で割った余りが0の場合は4の倍数です。

0 0 0 0 0 0 0 0 … 0  0 / 4 = 0 ... 0
0 0 0 0 0 0 0 1 … 1  1 / 4 = 0 ... 1
0 0 0 0 0 0 1 0 … 2  2 / 4 = 0 ... 2
0 0 0 0 0 0 1 1 … 3  3 / 4 = 0 ... 3
0 0 0 0 0 1 0 0 … 4  4 / 4 = 1 ... 0
0 0 0 0 0 1 0 1 … 5  5 / 4 = 1 ... 1
0 0 0 0 0 1 1 0 … 6  6 / 4 = 1 ... 2
0 0 0 0 0 1 1 1 … 7  7 / 4 = 1 ... 3
0 0 0 0 1 0 0 0 … 8  8 / 4 = 2 ... 0
0 0 0 0 1 0 0 1 … 9  9 / 4 = 2 ... 1

 これにヒントの色を加えます。

0 0 0 0 0 0 0 0 … 0  0 / 4 = 0 ... 0
0 0 0 0 0 0 0 1 … 1  1 / 4 = 0 ... 1
0 0 0 0 0 0 1 0 … 2  2 / 4 = 0 ... 2
0 0 0 0 0 0 1 1 … 3  3 / 4 = 0 ... 3
0 0 0 0 0 1
0 0 … 4  4 / 4 = 1 ... 0
0 0 0 0 0 1 0 1 … 5  5 / 4 = 1 ... 1
0 0 0 0 0 1 1 0 … 6  6 / 4 = 1 ... 2
0 0 0 0 0 1 1 1 … 7  7 / 4 = 1 ... 3
0 0 0 0 1 0
0 0 … 8  8 / 4 = 2 ... 0
0 0 0 0 1 0 0 1 … 9  9 / 4 = 2 ... 1

 4の倍数判定は奇数偶数判定の応用です。2進数での最下位2ビットの値が00の場合に4の倍数になります。
 つまり、下位2ビットに注目するだけで4の倍数を判断することができるのです。ここでも論理積(AND)の出番です。

 下位2ビットに注目する…この場合、利用する値は03Hです。
 では、試しに2を演算してみましょう。

0 0 0 0 0 0 1 0 … 02H
0 0 0 0 0 0 1 1 … 03H
---------------
0 0 0 0 0 0 1 0 … 02H = 02H & 03H

 結果は02Hになりました。00H以外の結果ですので2は4の倍数ではありません。
 続いて、6を演算します。

0 0 0 0 0 1 1 0 … 06H
0 0 0 0 0 0 1 1 … 03H
---------------
0 0 0 0 0 0 1 0 … 02H = 06H & 03H

 この場合も結果は02Hになりました。00H以外の結果ですので6は4の倍数ではありません。
 更に、8を演算します。

0 0 0 0 1 0 0 0 … 08H
0 0 0 0 0 0 1 1 … 03H
---------------
0 0 0 0 0 0 0 0 … 00H = 08H & 03H

 この場合は結果が00Hになりました。00Hですので8は4の倍数です。
 既に気がついている人もいると思います。03Hで論理積(AND)した結果は4で割った時の余りに等しくなります

 この手続きをプログラム風(C言語風であって、C言語ではありません)に記述すると、
  if (A & 03H)
となります。割り算を使用しないので高速に判断を行うことができます。

 ところで、03Hが4-1であることに気が付きましたか?
 この4をGRPと宣言すれば、

  if (A & (GRP - 1))
となります。

 今回は4の倍数の例ですが、2の階乗の値であるならば倍数判断は容易です。例えば8や16でも応用できます。

 難点は2の階乗以外の倍数については判断できないことです。例えば10や100の倍数判断の場合です。
 これについては特殊な方法で割り算を使用せずに判断する方法がありますが、別の章で説明したいと思います。


Copyright 2007-2009 PaletteSoft LLC. All rights reserved. 利用条件 | HOME | サイトマップ | お問合せ