Powered by AppSignal & Oban Pro

Bitwise

notebooks/bitwise.livemd

Bitwise

Introduction

https://qiita.com/mnishiguchi/items/1e0a1dd8de64dbb95d62

Bitwiseモジュールの関数を使用して、整数に対してビット単位のバイナリ関数を実行できます。

function operator
bitwise AND band/2 &&&
bitwise OR bor/2 `
bitwise SHIFT LEFT bsl/2 <<<
bitwise SHIFT RIGHT bsr/2 >>>
bitwise XOR bxor/2 なし
bitwise NOT bnot/1 なし

資料

ビット演算

import Bitwise

bitwise NOT

image credit: https://en.wikipedia.org/wiki/Negation

NOT 0111  (decimal 7)
  = 1000  (decimal 8)
-0b1000 = bnot(0b0111)
# もとの数を n とすると bnot(n) は -n-1 の値を返す
-0b0111 - 1
0b1000 = abs(bnot(0b0111))

整数の二進法表記を確認したい場合は、Integer.to_string/2もしくはinspect/2が便利です。どのフラグが立っているのか視覚的に確認できて有用です。

"1000" = 8 |> Integer.to_string(2)
"0b1000" = 8 |> inspect(base: :binary)

bitwise AND

image credit: https://en.wikipedia.org/wiki/Logical_conjunction

    0101 (decimal 5)
AND 0011 (decimal 3)
  = 0001 (decimal 1)
0b0001 = 0b0101 |> band(0b0011)
0b0001 = 0b0101 &&& 0b0011

bitwise OR

image credit: https://en.wikipedia.org/wiki/Logical_disjunction

   0101 (decimal 5)
OR 0011 (decimal 3)
 = 0111 (decimal 7)
0b0111 = 0b0101 |> bor(0b0011)
0b0111 = 0b0101 ||| 0b0011

bitwise XOR

image credit: https://en.wikipedia.org/wiki/Exclusive_or

    0101 (decimal 5)
XOR 0011 (decimal 3)
  = 0110 (decimal 6)
0b0110 = 0b0101 |> bxor(0b0011)

Guard

Bitwiseモジュールのすべての関数は、ガードとして使用できます。

odd? = fn
  int when Bitwise.band(int, 1) == 1 -> true
  _ -> false
end

odd?.(1)

ビットマスク

ビットマスクをつかってできること

  • 特定のビットをオン(1)やオフ(0)を反転
  • 特定のビットの状態(1または0)を知る

https://ja.wikipedia.org/wiki/マスク_(情報工学)

ビットマスクを作る関数を用意します。最下位ビット(LSB)の位置を基準とし、そこからの相対位置(オフセット)のビットをたてます。

import Bitwise

mask = fn bit_position ->
  1 <<< bit_position
end

この関数を用いて各ビットを操作するビットマスクを作ります。

0b00000001 = mask.(0)
0b00000010 = mask.(1)
0b00000100 = mask.(2)
0b00001000 = mask.(3)
0b00010000 = mask.(4)
0b00100000 = mask.(5)
0b01000000 = mask.(6)
0b10000000 = mask.(7)

複数のビットを操作したい場合は、ビットマスクを組み合わせることができます。

0b00101010 = mask.(1) ||| mask.(3) ||| mask.(5)

特定のビットをオン

add_flags_by_mask = fn uint8, mask ->
  uint8 ||| mask
end

# 最下位ビットから2番目と4番目と6番目のビットをオンにする
0b00101010 =
  0b00000000
  |> add_flags_by_mask.(mask.(1) ||| mask.(3) ||| mask.(5))

特定のビットをオフ

remove_flags_by_mask = fn uint8, mask ->
  uint8 &&& bnot(mask)
end

# 最下位4ビットをオフにする
0b11110000 =
  0b11111111
  |> remove_flags_by_mask.(mask.(0) ||| mask.(1) ||| mask.(2) ||| mask.(3))

特定のビットを反転

invert_flags_by_mask = fn uint8, mask ->
  uint8 |> bxor(mask)
end

# 最下位ビットから2番目と4番目のビットを反転する
0b00000010 =
  0b00001000
  |> invert_flags_by_mask.(mask.(1) ||| mask.(3))

特定のビットの状態を知る

get_bit_by_mask = fn
  uint8, mask when (uint8 &&& mask) == 0 -> 0
  _, _ -> 1
end

# 最下位ビットから4番目のビットがオンであるか確認
1 = 0b00001000 |> get_bit_by_mask.(mask.(3))
0 = 0b00000000 |> get_bit_by_mask.(mask.(3))
get_bit_at = fn
  uint8, index when (uint8 &&& 1 <<< index) == 0 -> 0
  _, _ -> 1
end

# 最下位ビットから4番目のビットがオンであるか確認
1 = 0b00001000 |> get_bit_at.(3)
0 = 0b00000000 |> get_bit_at.(3)
<<_::4, x::1, _::3>> = <<0b00001000>>
1 = x

<<_::4, x::1, _::3>> = <<0b00000000>>
0 = x
get_bit_at = fn bits, index when is_bitstring(bits) ->
  <<_::size(index + 1), bit::1, _::bitstring>> = bits
  bit
end

1 = <<0b00001000>> |> get_bit_at.(3)
0 = <<0b00000000>> |> get_bit_at.(3)