ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

06CU2の愉快な仲間たち(改)コミュのビット演算・進数変換

  • mixiチェック
  • このエントリーをはてなブックマークに追加
企業の筆記試験でよく出るようなので
にわか知識でちょっくら解説

コメント(10)

とりあえずビット演算から
8桁の数字の列は2進数

ビット演算子 & | ^ ~ << >>

& は論理積。かけた値が計算結果になっているから論理「積」
1 & 1 → 1
1 & 0 → 0
0 & 1 → 0
0 & 0 → 0

2進数の計算例

11110000
10101010
--------
10100000



| は論理和
1 | 1 → 1
1 | 0 → 1
0 | 1 → 1
0 | 0 → 0

2進数の計算例

11110000
10101010
--------
11111010



^ は排他論理和。論理和の事象から論理積の事象を取り除くので「排他」論理和
1 ^ 1 → 0
1 ^ 0 → 1
0 ^ 1 → 1
0 ^ 0 → 0

2進数の計算例

11110000
10101010
--------
01011010



~ はnot。ビットの反転(補数)を表す。 ! ではないことに注意
~00010001 → 11101110

<< は左シフト。数値分ビットがシフトする
詰められた部分は消え、増えた部分は0が入る
00000001 << 1 → 00000010
11110001 << 3 → 10001000

>> は右シフト
00000010 >> 1 → 00000001
10001111 >> 2 → 00100011


not以外の演算子には複合代入演算子が使える

a &= b;
a |= b;
a ^= b;
a <<= 3;
a >>= 2;

実際に変数に代入するときは2進数で代入できないので
16進数などで代入することになる

01111010 → 0x7A(16進)
unsigned char a = 0x7A;

00000100 → 1<<2
unsigned char a = 1<<2;
実際出た試験問題


#define POIZON 1<<0 //毒
#define PARALYZE 1<<1 //麻痺
#define SILENCE 1<<2 //沈黙
#define CONFUSE 1<<3 //混乱

unsigned char status;

であるとき次の操作はどう記述すればよいか

毒状態を付加したい

麻痺状態を解除したい

沈黙状態であるか調べたい

何か状態異常になっているか調べたい




以下、合ってるかどうか分からない「回答」

毒状態を付加したいとき
他の状態異常を表すビットを変化させずに
毒状態である最下位ビットを現在の値に因らず 1 にしてやる

POIZON → 00000001

status |= POIZON;
(毒を表す最下位ビットは 0 でも 1 でも 1 になり、他のビットは値が保持される)


麻痺状態を解除したい

PARALYZE → 1<<1 → 00000010
~PARALYZE → 11111101

status &= ~PARALYZE;
(麻痺を表すビットは 0 でも 1 でも 0 になり、他ビットは値が保持される)


沈黙状態であるか調べたい
if(status & SILENCE == SILENCE)
(沈黙を表すビットはそのまま、他ビットは全て 0 になる)

何か状態異常になっているか調べたい
if(status & 0x0F != 0)
(状態異常を表すビットがどこかひとつでも 1 なら 0x00 にはならない)
ちょっと訂正

演算子の順序に問題があるそうなので
if((status & SILENCE) == SILENCE)

if は 0 か 0 以外かの判断しかしないので
if(status & SILENCE)
でも可

あと「他ビットは全て 0 になる」と書きましたが
代入しているわけではないので
この if 文で status の値自体は変わりません
今更どうでもいいけどタイトル「進数変換」じゃなくて「基数変換」だった


基数変換
2進数 → 10進数

2進数の右端から順に
1
2
4
8
16
32
64
128
…
と2の累乗をかけて足す

110010 の場合
1*0 + 2*1 + 4*0 + 8*0 + 16*1 + 32*1 = 50


2進数 → 16進数

2進数を4つずつに区切り(足りない部分は0で補完)16進に直す
変換の方法は2進→10進と同じ、10〜15までをアルファベットに置き換える
丸暗記しててもいい
(最低でも 1010 は 10 → A ぐらいは覚えておくと便利)

1101011 の場合
0110 と 1011 に分解
0110 → 6
1011 → 11 → B

16進は最後に H をつけて表すことがある
1101011 → 6B(H)

プログラムでは 0x を頭につけると16進の意
0x6B


10進数 → 2進数

10進をひたすら2で割り、その余りが2進数になる(?)
うまく説明できないので例を見てわかって欲しい…

25の場合

25
12 ... 1
6 ... 0
3 ... 0
1 ... 1

11001


135 の場合

135
67 ... 1
33 ... 1
16 ... 1
8 ... 0
4 ... 0
2 ... 0
1 ... 0

10000111


16進数 → 2進数

16進の1桁を2進の4桁に直していく
丸暗記するか、手で数えるか、分解してみるか

3AC(H) の場合

3 → 2 + 1 → 0011
A → 1010 (暗記)
C → 12 → 8 + 4 → 1100

3AC(H) → 1110101100


10進数 → 16進数 / 16進数 → 10進数

2進数を経由して変換

125 の場合

125
62 ... 1
31 ... 0
15 ... 1
7 ... 1
3 ... 1
1 ... 1

125 → 1111101 → 0111 1101 → 7D(H)


A3(H)の場合

A3(H) → 1010 0011 → 1+2+32+128 → 163


2進数 → 8進数 / 8進数 → 2進数

2進の3桁が8進の1桁に相当
16進とあまり変わらない

11110010(2進) → 011 110 010 → 362(8進)
234(8進) → 010 011 100 → 10011100(2進)


8進数 ⇔ 16進数 / 8進数 ⇔ 10進数
2進数を経由
2の補数

符号付2進を10進に直す方法

unsigned ではない宣言での int や char は、一番左のビットが符号を表し、
0 だとプラス、1 はマイナスになる

signed char の場合 (普通に char とだけ宣言した場合)
00000001 は 1 になるが
10000001 は 129 ではなく -127 になる

2の補数の計算方法は、全てのビットを反転させて 1 を足すだけ
10000001 → 01111110 → 01111111 → 127
これにマイナスがついて -127 (このマイナスを付け忘れないように注意)

負の10進数から2進数への変換のやり方は
まず符号無しで変換し、2の補数を計算する

-40 の場合

40
20 ... 0
10 ... 0
5 ... 0
2 ... 1
1 ... 0

101000 → 00101000 → 11010111 → 11011000
(8桁に直す → 反転 → 1 を足す)

試しに 40 と -40 を足してみると 0 になる
(8桁を超えた分はなくなる)

00101000 40
11011000 -40
--------
00000000




ビットシフトでの倍数

<< を使ってビットシフトしたとき、2^(シフト数)倍になる
1 << 3 → 00000001 << 3 → 00001000 → 1 * 2^3 → 1 * 8 → 8
7 << 4 → 00000111 << 4 → 01110000 → 7 * 2^4 → 7 * 16 → 112

あふれた分はなくなってしまうことに注意
80 << 2 → 01010000 << 2 → 01000000 → 64

>> を使ってビットシフトしたとき、1/2^(シフト数)倍になる(端数切り捨て)
12 >> 2 → 00001100 >> 2 → 00000011 → 12 * 1/2^2 → 12 * 1/4 → 3
80 >> 3 → 01010000 >> 3 → 00001010 → 80 * 1/2^3 → 80 * 1/8 → 10
63 >> 4 → 00111111 >> 4 → 00000011 → 63 * 1/2^4 → 63 * 1/16 → 3

符号付で計算している場合、全く意図しない値になることもある
-126 >> 1 → 10000010 >> 1 → 01000001 → 65
40 << 2 → 00101000 << 2 → 10100000 → -96


ビットシフトで計算した方が、単純に2の累乗をかけるより速いので
a *= 4;
とせずに
a <<= 2;
と書いたりすることがある
すいません
符号付で右シフトするとシフト前の左端と同じ数が補填されるようです
なので正しくは

-126 >> 1 → 10000010 >> 1 → 11000001 → -63

になります
尚、符号無しだと

130 >> 1 → 10000010 >> 1 → 01000001 → 65

になります
実際あった試験問題

正確な数は覚えてないのでこんな感じだった程度
(1)と(2)は1バイトの符号付変数に格納されているものとする
つまり2進数の左端のビットが1ならマイナスの値を表す


(1)空欄を埋めなさい

2進数   10進数  16進数
11010110  (  )  (  )
(   )    -35   (  )
(   )    (  )  64(H)


(2)次の計算結果を10進数で記述しなさい

01110010 - 85
7D(H) + 10100101
53(H) & 106
-102 ^ CB(H)


(3)空欄を埋めなさい

50000ビット = (  )バイト
300000(H)バイト = (  )メガバイト






よく考えたら回答書かなくても関数電卓で答え合わせできる罠

(1)

11010110 → 00101010(2の補数) → -42
11010110 → 1101 0110 → D6(H)

-35 → -00100011 → 11011101(2進) → DD(H)

64(H) → 01100100 → 100


(2)

01110010 - 85 = 114 - 85 = 29

7D(H) + 10100101
(2進でも10進に直しても計算できる)
01111101  125
10100101  -91
--------
00100010 → 34

53(H) & 106
01010011
01101010
--------
01000010 → 66

-102 ^ CB(H) (排他論理和 xor)
10011010
11001011
--------
01010001 → 81


(3)

1バイト = 8ビット

50000ビット = 50000/8バイト = 6250バイト


1キロバイト = 1024バイト = 2^10バイト
1メガバイト = 1024キロバイト = 2^10キロバイト
1メガバイト = 1024*1024バイト = 2^20バイト

16進の1桁は2進の4桁に相当するので、16進のゼロ5個は2進のゼロ20個

300000(H)バイト = 1100000000000000000000(2進)バイト
= 3 << 20 バイト = 3*2^20 バイト = 3メガバイト

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

06CU2の愉快な仲間たち(改) 更新情報

06CU2の愉快な仲間たち(改)のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。