...

ビット演算

by user

on
Category: Documents
47

views

Report

Comments

Transcript

ビット演算
ビット演算
〜 アルゴリズムとデータ構造及び演習 〜
2進数
計算機のデータはすべて2進数で扱われる
最上位ビットを MSB (Most Significant Bit)、最下位
ビットを LSB (Least Significant Bit) と呼ぶ。
0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 1 0 1 0
↑
↑
MSB
LSB
整数として見ると上の例は(10進数で)10
3
2
×1+
2
2
×0+
1
2
×1+
0
2
× 0 = 10
ビット演算
2進データを0と1のビットの列(ビットパターン)
と⾒て、これに対する操作を⾏う。
整数値に逐⼀還元して考える必要はない。
C⾔語には、ビットパターンを操作するための演算
⼦が⽤意されている。
• ビットごとの積(AND)
• 和(OR)、排他的論理和(XOR: exclusive OR)
• 否定(NOT)
• シフト演算
どんな時にビット演算が必要か (1)
OSや周辺機器とやりとりするデータは、1ワード内
にビット単位で情報が格納されていることがある。
• 例:UNIXの stat() が返すファイル情報の⼀部
1 0 0 0 0 0 0 1 1 0 1 0 0 10 0
rwx rwx rwx
ファイルの種類
他人のアクセス
グループ内のアクセス
set user id
所有者のアクセス
set group id
sticky bit
どんな時にビット演算が必要か (2)
ビット単位で表現されたデータを扱う場合。例えば、
画像データは数ビットで1画素を表すことがある。
通信のための符号化や圧縮、暗号化の処理はビット
の列に対して処理を⾏う。
⼀般に、ビット演算は四則演算よりも⾼速なため、
計算の⼀部を(可能なら)ビット演算で置き換える
ことがある。
8進数、16進数
2進数そのままでは扱いにくいので、プログラムでは8
進数や16進数として表記する。
C⾔語では、037 のように数値を0から書き始めると8
進数になる。16進数は 0xa1 のように 0x を先頭に付け
る(a-f、A-F はどちらでもよい)。
ビットを3つずつまとめると8進数、4つずつまとめる
と16進数になる。
0
0
0
4
C
3
8
A
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 11 0 0 0 0 1 1 1 0 0 0 1 0 1 0
0
0
0
0
1
1
4
1
6
1
2
ビットごとの論理積 “&”
Cの演算⼦では、2項演算⼦としての “&”。
対応するビットごとに AND演算を⾏う。
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
&
0x0693
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0x0682
0 0 0 0 0 1 1 0 1 0 00 0 0 1 0
複合代入: a &= 1 は a = a & 1 と同じ。
ビットごとの論理和 “|”
Cの演算⼦では、2項演算⼦ “|”。
対応するビットごとに OR演算を⾏う。
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
|
0x0693
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0x0edb
0 0 0 0 1 1 1 0 1 1 01 1 0 1 1
複合代入: a |= 1 は a = a | 1 と同じ。
ビットごとの排他的論理和 “^”
Cの演算⼦では、2項演算⼦ “^”。
対応するビットごとに XOR演算を⾏う。
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
^
0x0693
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0x0859
0 0 0 0 1 0 0 0 0 1 01 1 0 0 1
複合代入: a ^= 1 は a = a ^ 1 と同じ。
ビットごとの論理否定 “~”
Cの演算⼦では、単項演算⼦ “〜”。
各ビットを反転する(0は1に、1は0になる)。
0x0693
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0xf96c
111 1 1 0 0 1 0 1 10 1 1 0 0
シフト演算 (1)
ビットパターンを指定した桁数だけ左右にずらす。
n
左シフトを n 桁⾏うと、整数値としては 2 倍したのと同
n
じことになる(右シフトは 2 で割って余りを切り捨
て)。
C⾔語の演算⼦では、左シフト、右シフトはそれぞれ2項
演算⼦ “<<” と “>>”。
a = v << 1; /* v のビットパターンを左へ1ビット
シフトした値を a へ代入 */
複合代入: a <<= 1 は a = a << 1 と同じ。
シフト演算 (2)
左シフト
0x0693 << 2
0x1a4c
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0 0 0 1 1 0 1 0 01 0 0 1 10 0
右シフト
0x0693 >> 4
0x0069
0 0 0 0 0 1 1 0 1 0 01 0 0 1 1
0 0 0 0 0 0 0 0 0 1 1 0 1 0 01
シフト演算 (3)
符号拡張
符号付き整数を右シフトする場合、MSB側
には符号ビットと同じビットが⼊れられる。
short a = 0x8693;
a >> 4
1 0 0 0 0 1 1 0 1 0 01 0 0 1 1
1 1 1 1 1 0 0 0 0 1 1 0 1 0 01
unsigned short b = 0x8693;
1 0 0 0 0 1 1 0 1 0 01 0 0 1 1
b >> 4
0 0 0 0 1 0 0 0 0 1 1 0 1 0 01
演算⼦の優先順位
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
( ) [ ] ‐> . 後置++ 後置‐‐
! ~ ++前置 ‐‐前置 *アドレス &アドレス sizeof
(型)
* / %
+ ‐
<< >>
< <= > >=
C言語のビット演算子の優先
== !=
順位は少々おかしい。
ビット積 &
ビット排他的論理和 ^
括弧を付けて分かりやすく書
ビット和 |
&&
くようにする。
||
?:
= += ‐= *= /= %= &= |= ^= <<= >>=
,
AND演算のよくある使い⽅(1)
特定のビット位置が0か1か(ビットが⽴ってい
るかどうか)を調べる
•
if (info & 0x20) { ...
infoの、右から6ビット⽬が1なら ...
•
if (s & 1) { ...
s が奇数なら ...
•
if ((s & 1) == 0) { ...
s が偶数なら ... /* 注意:括弧が必要 */
AND演算のよくある使い⽅(2)
特定のビット範囲だけ取り出す
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
&
0x00f0
0 0 0 0 0 0 0 0 1 1 1 1 0 0 00
0x00c0
0 0 0 0 0 00 0 1 1 0 0 0 0 0 0
1を並べた部分だけ、元のデータと同じビット列に
なり、ほかは0になることが分かる。このようにし
て、必要な部分を指定するためのビットパターンを
「マスク」と呼ぶ。
AND演算のよくある使い⽅(3)
特定のビット範囲だけ0にする
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
&
0xff0f
111 111 1 1 0 0 00 1 1 1 1
0x0e0a
0 0 0 0 1 1 1 0 0 0 00 1 0 1 0
マスクの役割が (2)と逆になった場合。
OR演算のよくある使い⽅(1)
特定のビット範囲を1にする
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
|
0x0060
0 0 0 0 0 0 0 0 0 1 1 0 0 0 00
0x0eea
0 0 0 0 1 1 1 0 111 0 1 0 1 0
元のビットパターンが何であっても、マスクで1が
立っている部分は1になる。
OR演算のよくある使い⽅(2)
特定の範囲にビットパターンをセットする
0x0e0a
0 0 0 0 1 1 1 0 0 0 00 1 0 1 0
|
0x0060
0 0 0 0 0 0 0 0 0 1 1 0 0 0 00
0x0e6a
0 0 0 0 1 1 1 0 011 0 1 0 1 0
ビットパターンをセットしたい部分は、あらかじめ
0でクリアしておく必要がある。
XOR演算のよくある使い⽅
特定のビットだけ反転させる
0x0eca
0 0 0 0 1 1 1 0 1 1 00 1 0 1 0
^
0x0060
0 0 0 0 0 0 0 0 0 1 1 0 0 0 00
0x0eaa
0 0 0 0 1 1 1 0 1 0 10 1 0 1 0
マスクで0の部分はそのまま、1が立っている部分
は反転する(0は1に、1は0になる)。
マスクの作り⽅(1)
マスクの作り⽅はいくつも考えられる。
もちろん、nやmが決まって
いれば、定数を使えばよい。
0
そうでない場合の例。
...
n
m
0 1 ... 1 0
...
0
n
a = 1 << n;
0
...
0 1 0 ... 0
n
a = a ‐ 1;
a = a << m;
0
0
...
...
0 1 ... 1
n
m
0 1 ... 1 0
...
0
マスクの作り⽅(2)
四則演算を使わない作り⽅の例。
特定機種の整数のビット数
に依存しないで、右のよう
なマスクを作る例。
a = ~0;
m
1
...
1
10
...
...
0
1
m
a = a << m;
1
...
10
...
0
ビット演算を算術演算として使う
n
整数に対する 2 の乗算、除算の代わりにシフト
演算を使う
a * 256 ⇔ a << 8
a / 128 ⇔ a >> 7
n
整数を 2 で割った余りを求めるのに & を使う
a % 256 ⇔ a & 255 ⇔ a & 0xff
上記のような記述は慣⽤的に広く⽤いられている
n
2 で割った余りを求める
AND演算で下位nビットの範囲だけ取り出す
0x0aca
0 0 0 0 1 0 1 0 1 1 00 1 0 1 0
&
0xff
0 0 00 0 0 0 0 111 1 1 1 1 1
0xca
0 0 0 0 0 0 0 0 1 1 00 1 0 1 0
図は、256 (28) で割った余りを求める例。
0x100 = 256
0 0 0 0 0 0 0 100 0 0 0 0 0 0
256の倍数は、上の白い部分が必ず0であることに注意。
整数を16進数で印字する例
void print_hex(int val)
{
static char hex[] = “0123456789abcdef”;
int i;
for (i = (sizeof(int) * 8 - 4);
i >= 0; i -= 4) {
putchar(hex[(val >> i) & 0x0f]);
}
}
まめ知識
ここはこう書くこともできる
“0123456789abcdef”[(val >> i) & 0x0f]
整数を16進数で印字する例(説明)
int型が2バイト(16ビット)で、val = 0x175a の時
i = (sizeof(int) * 8 - 4); = 12
val
0 0 0 1 0 1 1 1 0 1 01 1 0 1 0
val>>12
0 0 0 0 0 0 0 0 0 0 00 0 0 0 1
& 0xf = 1
val>>8
0 0 0 0 0 0 0 0 0 0 01 0 1 1 1
& 0xf = 7
val>>4
0 0 0 0 0 0 0 1 0 1 11 0 1 0 1
& 0xf = 5
val>>0
0 0 0 1 0 1 1 1 0 1 01 1 0 1 0
& 0xf = 10
Fly UP