Perlゼミ(サンプルコードPerl入門)

2008-08-06

ビット演算子

  1. Perl
  2. 演算子
  3. ビット演算

ビット演算とは、ビット単位の論理和、論理積を求める演算のことです。ビット演算を使用する機会は少ないですが、sysopenなどC言語のライブラリを直接利用した関数を使う場合に、ビット演算を行うことがあります。またビット単位の低レベルなデータ操作を行いたい場合に使うことがあります。

ビット演算には、論理積、論理和、排他的論理和、否定があります。

演算子意味ビット演算の例ビット演算の結果ビット演算の解釈
&論理積1100 & 10101000両方が1なら1、そうでなければ0
|論理和1100 | 10101110どちらかが1ならば1,そうでなければ0
^排他的論理和1100 ^ 10100110どちらか一方だけが1ならば1,そうでなければ0
~否定^11000011ビットを反転させる

2進数、16進数、10進数の対応

2進数10進数
000101
001002
001103
010004
010105
011006
011107
100008
100109
101010
101111
110012
110113
111014
111115

2進数の繰り上がりと10進数

2進数10進数
000000011
000000102
000001004
000010008
0001000016
0010000032
0100000064
10000000128

10進数で2倍になることと、2進数で位がひとつ上がることが対応しています。

ビットシフト演算子

ビットシフトとは、ビットの並らびを左右にずらすことです。ビットシフトには右ビットシフトと左ビットシフトがあります。数学の視点で見ると、左ビットシフトは2倍、右ビットシフトは 2で割った商と同じです。

演算子意味
>>右ビットシフト
<<左ビットシフト
ビットシフト演算子
# 左ビットシフト
$num << 1;
$num << 2;
$num << 3;

# 右ビットシフト
$num >> 1;
$num >> 2;
$num >> 3;

<< と >> が、ビットシフト演算子です。指定した数値だけ、シフト演算が行われます。

上記の2進数は10進数ではいくつになるでしょうか。位とビットの値から以下のように計算できます。

76543210
ビット00100100
2の7乗×0 +
2の6乗×0 +
2の5乗×1 +
2の4乗×0 +
2の3乗×0 +
2の2乗×1 +
2の1乗×0 +
2の0乗×0

すなわち

128 × 0 +
 64 × 0 +
 32 × 1 +
 16 × 0 +
  8 × 0 +
  4 × 1 +
  2 × 0 +
  1 × 0 +
= 32 + 4 = 36

左ビットシフトの意味

左ビットシフトは、値を2倍にすることに等しいです。

76543210
ビット00100100

左ビットシフトさせると

76543210
ビット01001000

計算すると

128 × 0 +
 64 × 1 +
 32 × 0 +
 16 × 0 +
  8 × 1 +
  4 × 0 +
  2 × 0 +
  1 × 0 +
= 64 + 8 = 72

で確かに2倍になります。

反対に右ビットシフトを行うと、2で割った商と同じです。

コンピュータにおけるビットの意味を理解する

ビットの意味自体がわからないという方は、こちらをご覧ください。

ビットとは、コンピュータが扱うデータの最小単位です。ビットの状態には、「立っている」か「立っていない」かの2通りしかありません。2進数で表現すると、「1」か「0」かということです。1と0並びはビット列と呼ばれます。

コンピュータの装置には、メモリと呼ばれるものがありますが、メモリとは巨大なビット列を保存する装置です。

電球のたとえ

メモリをイメージするのは最初は難しいと思いますが、電球が一直線にたくさん並んでいるものと思えばイメージしやすいと思います。たくさんの電球の並びがあって「ついている電球」と「ついていない電球」があります。

○×○×○○○×

上記のように、電球が8個あるとします。ひとつの電球で「ついている状態」と「ついていない状態」を表現できます。つまり、ひとつの電球で2通りの表現ができます。8つあれば、2の8乗で、256通りの表現ができます。

電球を用いた情報伝達

電球を使って情報を伝達してみます。AさんとBさんがいて、電球のつきかたについて次のような取り決めを行ったとしましょう。

○○○○×××× → 助けてくれ。
×○○○○××× → 無事についた。

こうすれば、AさんとBさんは、夜の何も見えない場所、遠くて音が聞こえない場所でも、電球を使って情報をやり取りすることができます。「○○○○××××」のような単独では無意味な記号に、人間が意味を与えることで情報をやり取りできます。

電球の並びに2進数という数学的な意味を与える

電球のならびに、2進数という数学的な意味を与えます。

×××××××× → 00000000
×××××××○ → 00000001
○××××○×○ → 10000101

こうすることで、単なる電球の並びが、数学的な意味を持つようになります。これがビット列と呼ばれ、コンピュータの計算の基礎になります。

サンプルコード

以下は、ビット演算のサンプルと、ビットでフラグを表現するサンプルです。

use strict;
use warnings;

# 0b を数字の前につけると2進数で表現できます。
# %04b という書式指定で4桁左0埋めの
# 2進数表現を出力できます。
print "1: 2進数1100と1010の論理積\n";
printf("%04b", 0b1100 & 0b1010); 

print "\n";

print "2: 2進数1100と1010の論理和\n"; 
printf("%04b", 0b1100 | 0b1010);
print "\n";

print "3: 2進数1100と1010の排他的論理和\n";
printf("%04b", 0b1100 ^ 0b1010);
print "\n";

# Perlは数値を倍精度の浮動小数点(32ビット)
# で表現するので、32bitすべてのビットが
# 反転する。
print "4: 2進数1100の否定\n";
printf("%04b", ~0b1100); 
print "\n\n";

print "5: ビット演算を用いてすべてのフラグがオンであることを表\現する。\n";
sub FLG1{ 1; } # 1を返すサブルーチン。1は2進数では、 1。
sub FLG2{ 2; } # 2を返すサブルーチン。2は2進数では、10。
sub FLG3{ 4; } # 4を返すサブルーチン。4は進数では、100。

my $all_flg_on = FLG1 | FLG2 | FLG3;
printf("%03b", $all_flg_on);
print "\n\n";

print "6: FLG3がオンであることを確認する。\n";
if ($all_flg_on & FLG3) {
  print "FLG3はオンになっています。\n";
}

printf("%04b", 0b1100 & 0b1010); 
(1)2進数で数字を表現する

0bというプレフィックスを使うことで、2進数で数値を表現することができます。

(2)2進数表現で出力する

2進数で出力するには、printf関数を用いて、書式に%bを指定します。%とbの間にある04というのは、4桁表示で、満たない部分を0で埋めるということを意味するオプションです。

(3)ビット演算でフラグを表現する
sub FLG1{ 1; } # 1を返すサブルーチン。1は2進数では、 1。
sub FLG2{ 2; } # 2を返すサブルーチン。2は2進数では、10。
sub FLG3{ 4; } # 4を返すサブルーチン。4は進数では、100。

my $all_flg_on = FLG1 | FLG2 | FLG3;
printf("%03b", $all_flg_on);
print "\n\n";

print "6: FLG3がオンであることを確認する。\n";
if ($all_flg_on & FLG3) {
  print "FLG3はオンになっています。\n";
}
(3)-1 サブルーチンで定数を表現する
sub FLG1{ 1 };

こう書くと、サブルーチンが呼び出されたときに1が返却され、定数のように扱うことができます。呼び出すときは、FLG1() と書かなくても、FGG1で呼び出すことができます。

(3)-2 ビットが重ならないようにフラグの値を2の倍数にする
sub FLG1{ 1; } # 1を返すサブルーチン。1は2進数では、 1。
sub FLG2{ 2; } # 2を返すサブルーチン。2は2進数では、10。
sub FLG3{ 4; } # 4を返すサブルーチン。4は進数では、100。

2の倍数を用いることで、ビットが重ならないようにします。フラグとして用いるためには、各ビットが別々に判断できる必要があります。

(3)-3 すべてのフラグがオンになっているという表現
my $all_flg_on = FLG1 | FLG2 | FLG3;

オンにしたいビットを論理和で結び付けます。

(3)-4 フラグがオンであることを確認する
if ($all_flg_on & FLG3) {
  # ...
}

オンであるかを確認したいフラグとの論理積を取ることで、フラグがオンになっているかを確認できます。

(3)-5 基本的にビット演算は避ける

基本的にビット演算を使うようなプログラムは書きません。ビット演算の必要が生じるのはC言語のライブラリを直接ラップしたPerlの関数を呼び出すときくらいです。

ビット演算の意義を書くと、メモリの節約と処理の高速化です。1ビットで判定できるので、少ないビットで多くの情報を表現できます。また、1条件につき1ビット判定するだけなので処理は高速になります。

以下は、ビットシフト演算のサンプルです。

use strict;
use warnings;

my $num = 8;
print "\$num = $num\n\n";

print "1: 左ビットシフト\n";
# 2倍と同じ
print "1ビット左シフト: " . $num << 1 . "\n";
# 2の2乗倍と同じ
print "2ビット左シフト: " . $num << 2 . "\n";
# 2の3乗倍と同じ
print "3ビット左シフト: " . $num << 3 . "\n";
print "\n";

print "2: 右ビットシフト\n";
# 2で割った商と同じ
print "1ビット右シフト: " . $num >> 1 . "\n";
# 2の2乗で割った商と同じ
print "2ビット右シフト: " . $num >> 2 . "\n";
# 2の3乗で割った商と同じ
print "3ビット右シフト: " . $num >> 3 . "\n";
print "\n";
2進数から10進数への変換
00100100

投稿したコメントは管理者が承認するまで公開されません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証