カテゴリー
シェルコマンドを使って学ぶビット演算(&、|、^、~等)の総復習
※ 当ページには【広告/PR】を含む場合があります。
2021/10/25
2022/01/21
基礎編
2進法を10進法に表示する
N#
2#
#👇2進法のつもり(10進法では86)
$ A=01010110
#👇これは当然Aの中身は文字として扱われる
$ echo $A
01010110
#👇2進法と明示して、10進法表示にする
$ echo $((2#${A}))
86
$((演算式))
$((演算式))
$ echo $((2#01010110))
86
10進法を2進法に表示する
bc
$ echo 'obase=2;ibase=10;86' | bc
1010110
#👇入力が10進法の場合、ibaseは省略可
$ echo 'obase=2;86' | bc
1010110
#👇文字列を分離したほうが見栄えが良い(かも)
$ echo 'obase=2;' 86 | bc
1010110
#👇echoを使いたくない場合、ヒアストリング(<<<)を使う
$ bc <<< 'obase=2;'86
1010110
printf
#👇8ビット表示
$ echo 'obase=2;' 86 | bc | xargs printf "%08d\n"
01010110
#ヒアストリング版
$ bc <<< 'obase=2;'86 | xargs printf "%08d\n"
01010110
ビット演算(レベル1)
4つの基礎論理演算子
記号 & ... AND演算子:
論理積。
両方1ならば1、それ以外は0を返す
記号 | ... OR演算子:
論理和。
どちらか一方が1ならば1、両方1ならば1、両方0は0を返す
記号 ^ ... XOR演算子:
排他的論理和。
どちらか一方が1ならば1、両方1か0ならば0を返す
記号 ~ ... NOT演算子:
1ならば0、0ならば1を返す。
補数表現での、~N = -(N+1)と等価
AND演算子
0110
1011
&
$ echo 'obase=2;' $((2#0110 & 2#1011)) | bc | xargs printf "%04d\n"
0010
0010
0 1 1 0
1 0 1 1
- - - -
0 0 1 0
OR演算
|
$ echo 'obase=2;' $((2#0110 | 2#1011)) | bc | xargs printf "%04d\n"
1111
0 1 1 0
1 0 1 1
- - - -
1 1 1 1
XOR演算
^
$ echo 'obase=2;' $((2#0110 ^ 2#1011)) | bc | xargs printf "%04d\n"
1101
0 1 1 0
1 0 1 1
- - - -
1 1 0 1
NOT演算子
~
$ echo 'obase=2;' $((~2#0110)) | bc
-111
0110
-111
0 1 1 0
= = = =
1 0 0 1
−(マイナス)
2進数での補数
−
0110
1001
0111
0 1 0 0 1
0 0 1 1 1
- - - - -
1 0 0 0 0
10000
0110
$ echo 'obase=2;' $((2#10000 - 2#0111)) | bc
1001
$ echo 'obase=2;' $((2#10000 + ~2#0110)) | bc
1001
関係式: ~N = -(N+1)
例:
~0110 = -0111
「ビット反転は補数なんだな」
-
i = ~i + 1;
//もしくは
i = (i ^ -1) + 1;
寄り道①〜オタマジャクシ演算子
オタマジャクシ演算子
-
-
-~
~-
関係式: -M = ~(M-1)
~-
関係式: ~-R = ~~(R-1) = R-1
※ ~~ は反転の反転で元に戻す操作
-~
関係式: -~S = --(S+1) = S+1
※ -- で補数の補数は元に戻す操作
$ echo 'obase=2;' $((~-2#0110)) | bc | xargs printf "%04d\n"
0101
$ echo 'obase=2;' $((-~2#0110)) | bc | xargs printf "%04d\n"
0111
i++
寄り道②〜Javascriptでは小数点以下切り落としに使う
~~
console.log(~~0.7);
console.log(~~3.54);
console.log(~~6317.4906);
console.log(~~-12.76);
console.log(~~-1532.076);
//👇実行結果
0
3
6317
-12
-1532
~~
ビットシフト
シフト演算
記号 << N ... 左シフト演算子:
各ビットをN桁左にシフトさせる。
算術的にはNビットのシフトで2のN乗倍することと等価
記号 >> N ... 右シフト演算子:
各ビットをN桁右にシフトさせる。
算術的にはNビットのシフトで1/2のN乗倍することと等価
#👇1ビット左シフト
$ echo 'obase=2;' $((2#0110 << 1)) | bc | xargs printf "%04d\n"
1100
#👇1ビット右シフト
$ echo 'obase=2;' $((2#0110 >> 1)) | bc | xargs printf "%04d\n"
0011
#👇sedで下位4ビットだけ取り出し
$ echo 'obase=2;' $((2#1111 << 3)) | bc | sed -r 's/.*(.{4})$/\1/'
1000
#👇(別解)revで下位4ビットを後ろ読みして戻す
$ echo 'obase=2;' $((2#1111 << 2)) | bc | rev | cut -c 1-4 | rev
1100
ビット演算(レベル2)
Nビット目の取得
(2進数 >> N) & 1
$ echo 'obase=2;' $(( (2#1011 >> 0) & 1 )) | bc
#もしくは echo 'obase=2;' $(( 2#1011 & 1 )) | bc
1
$ echo 'obase=2;' $(( (2#1011 >> 1) & 1 )) | bc
1
$ echo 'obase=2;' $(( (2#1011 >> 2) & 1 )) | bc
0
$ echo 'obase=2;' $(( (2#1011 >> 3) & 1 )) | bc
1
2進数 & (1 << N)
$ echo 'obase=2;' $(( 2#1011 & (1 << 0) )) | bc
1
$ echo 'obase=2;' $(( 2#1011 & (1 << 1) )) | bc
10
$ echo 'obase=2;' $(( 2#1011 & (1 << 2) )) | bc
0
$ echo 'obase=2;' $(( 2#1011 & (1 << 3) )) | bc
1000
Nビット目に書き込みをする
2進法 | (1 << N)
$ echo 'obase=2;' $(( 2#1011 | (1 << 2) )) | bc
1111
2進法 &~ (1 << N)
$ echo 'obase=2;' $(( 2#1011 &~ (1 << 1) )) | bc
1001
Nビット目を反転
2進法 ^ (1 << N)
$ echo 'obase=2;' $(( 2#1011 ^ (1 << 0) )) | bc
1010
全ビットの反転
$ echo 'obase=2;' $(( ~2#1011 )) | bc
$ echo 'obase=2;' $(( 2#1011 ^~ 0 )) | bc
-1100
0100
$ echo 'obase=2;' $((2#10000 - 2#1100)) | bc | xargs printf "%04d\n"
#👇でも良い
#$ echo 'obase=2;' $(( (1<<4) - 2#1100)) | bc | xargs printf "%04d\n"
0100
Nビットマスクの作成
(1 << N) - 1
$ echo 'obase=2;' $(( (1<<0) - 1)) | bc | xargs printf "%04d\n"
0000
$ echo 'obase=2;' $(( (1<<1) - 1)) | bc | xargs printf "%04d\n"
0001
$ echo 'obase=2;' $(( (1<<2) - 1)) | bc | xargs printf "%04d\n"
0011
$ echo 'obase=2;' $(( (1<<3) - 1)) | bc | xargs printf "%04d\n"
0111
余談〜c言語等で扱う時のオペレーター
uint8_t x = 0b00001011;
x = x | (1 << 2);
x |= (1 << 2);
|=
略記なし | 略記型 |
---|---|
a = a | b | a |= b |
a = a & b | a &= b |
a = a ^ b | a ^= b |
a = a << N | a <<= N |
a = a >> N | a >>= N |
ビット演算(レベル3)
2^Nの剰余を取得
二進法 & (1 << N) - 1
1101
$ echo 'obase=2;' $((13)) | bc
#👇10進法で13を2進法で
1101
$ echo 'obase=2;' $((13 & (1 << 3))) | bc
#echo 'obase=2;' $((2#1101 & (1 << 3))) | bc でも良い
#👇8(商の部分)
1000
$ echo 'obase=2;' $((13 & (1 << 3) - 1)) | bc
#echo 'obase=2;' $((2#1101 & (1 << 3) - 1)) | bc でも良い
#👇5(余剰部分)
101
count++;
count &= (1<<N) - 1;
#!/bin/bash
A=0
for i in `seq 16`; do
echo $(($A & (1 << 3) - 1)) | bc
A=$(echo 'obase=2;' $((-~2#$A)) | bc)
done
$ ./cycle.sh
0
1
2
3
4
5
6
7
0
1
2
3
4
5
6
7
等価性
#👇2値が一致する場合にはゼロを返す
$ echo 'obase=2;' $((2#0110 ^ 2#0110)) | bc
0
#👇2値が一致しない場合には非ゼロを返す
$ echo 'obase=2;' $((2#0101 ^ 2#0110)) | bc
11
//👇xとyが同じならtrue
(x ^ y) == 0;
//👇もしくは以下でも同じ
!(x ^ y)
偶奇性
#👇奇数の場合には1を返す
$ echo 'obase=2;' $((2#0101 & 1)) | bc
#echo $((7 & 1)) | bc と同じ
1
#👇偶数の場合には0を返す
$ echo 'obase=2;' $((2#0110 & 1)) | bc
#👇echo $((6 & 1)) | bc と同じ
0
//👇奇数の場合はtrue
(n & 1) == 1;
& 1
スワップ
//👇順に書くと
x ^= y;
y ^= x;
x ^= y;
//👇ワンライナーでスワップ
x^=y^(y=x);
まとめ
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー