【ビット演算入門】ビット演算の仕組み

## はじめに

最近、ビット演算について学ぶ機会がありました。最初は難しそうに思えたビット演算ですが、実はとてもシンプルで強力なツールであることがわかりました。ここではビット演算について、仕組みや利用シーンについて勉強したことをシェアしていきます。

## ビット演算とは？

**ビット演算**とは、数値を **ビット単位** で操作する演算方法です。数値はコンピュータ内部で **2進数**（0と1）で表されているため、ビットごとに計算を行うことで、効率的な処理が可能です。

### 各桁ごとに計算される
ビット演算は、**各ビット（桁）ごとに演算**が行われます。この動作は、10進数の演算において **同じ位同士を計算する**のをイメージするとわかりやすいです。10進数での加算では、1の位、10の位、100の位と順に計算が行われますが、**ビット演算では2進数の各ビットが順番に計算**されます。

例えば、2進数の `5`（0101）と `3`（0011）を **AND演算**（`&`）する場合、対応するビット同士を比較し、両方が1なら1、それ以外は0となります。

```plaintext
  0101   (5)
& 0011   (3)
  ----
  0001   (1)
```


## ビット演算の基本的な操作
ビット演算にはいくつかの基本的な演算子があります。それぞれ、2進数の各ビットごとに計算が行われます。

### AND（&）
両方のビットが 1 である場合、結果も 1 になります。それ以外は 0 です。
例：5 & 3 → 0101 & 0011 = 0001 → 結果は 1
```plaintext
  0101   (5)
& 0011   (3)
  ----
  0001   (1)
```
### OR（|）
どちらか一方のビットが 1 であれば、結果は 1 になります。
例：5 | 3 → 0101 | 0011 = 0111 → 結果は 7
```plaintext
  0101   (5)
| 0011   (3)
  ----
  0111   (7)
```
### XOR（^）
ビットが異なる場合に 1 になります。同じ場合は 0 です。
例：5 ^ 3 → 0101 ^ 0011 = 0110 → 結果は 6
```plaintext
  0101   (5)
^ 0011   (3)
  ----
  0110   (6)
```
### 左シフト（<<）
ビットを左にシフトし、右に 0 を埋めます。これは数値を2倍にする操作と等価です。
例：5 << 1 → 0101 << 1 = 1010 → 結果は 10
```plaintext
5 << 1
=> 0101 << 1 //0101の各桁の値を1つ左にずらす
=> 1010 (10)
```
### 右シフト（>>）
ビットを右にシフトし、符号ビットで埋めます。これは数値を2で割る操作と同じです（小数点以下は切り捨て）。
例：5 >> 1 → 0101 >> 1 = 0010 → 結果は 2
```plaintext
5 >> 1
=> 0101 >> 1 //0101の各桁の値を1つ右にずらす
=> 0010 (2)
```
## 具体的なビット演算の利用シーン
### 1. 偶数・奇数の判定
ビット演算を使うと、ある数値が **偶数か奇数か** を簡単に判定できます。二進数では、偶数は最下位ビットが `0`、奇数は `1` になっているため、最下位ビットを確認するだけで判定が可能です。

#### 具体例

```ts
function isEven(num: number): boolean {
    return (num & 1) === 0;
}

console.log(isEven(4));  // true（偶数）
console.log(isEven(5));  // false（奇数）
```
- **偶数**の最下位ビットは常に `0` で、**奇数**の最下位ビットは `1` です
- `num & 1` で最下位ビットが `0` か `1` かを確認します

### 2. 2で割る、掛ける操作
ビットシフト演算を使って、数値を **2で割る、掛ける** 操作を高速に行うことができます。右に1ビットシフトすると、数値が2で割られ、左に1ビットシフトすると、数値が2で掛けられ、小数点以下は切り捨てられます。

#### 右に1ビットシフトして 2で割る
```ts
function divideByTwo(num: number): number {
    return num >> 1;
}

console.log(divideByTwo(4));  // 2
console.log(divideByTwo(5));  // 2（小数点以下は切り捨て）

```
`num >> 1`: 数値を右に1ビットシフトすると、2で割る操作が行われます。小数点以下は切り捨てられます

#### 左に1ビットシフトして 2で掛ける 操作
```ts
function multiplyByTwo(num: number): number {
    return num << 1;
}

console.log(multiplyByTwo(4));  // 8（4 × 2 = 8）
console.log(multiplyByTwo(7));  // 14（7 × 2 = 14）
```
`num << 1`: 数値を左に1ビットシフトすると、2で掛ける操作が行われます。

- **右にビットシフト（>>）**: 数値を2で割る操作と同じです。ビットを1つ右に移動することで、数値が半分になります（小数点以下は切り捨てられます）
- **左にビットシフト（<<）**: 数値を2で掛ける操作と同じです。ビットを1つ左に移動することで、数値が2倍になります

## その他の利用シーン
ビット演算は、奇数・偶数判定や2の倍数の計算以外にもさまざまな場面で効率的に利用できます。

### 1. **ビットマスクを使った特定のビットの操作**
ビットマスクを用いることで、特定のビットを確認・設定・リセットすることが可能です。フラグ管理や権限のチェックなどで利用されます。

**利用シーン例**: フラグのON/OFF、権限管理、特定ビットの確認。

---

### 2. **フラグ管理**
1つの数値で複数のフラグ（ON/OFF状態）を管理し、効率的に状態を制御できます。ゲーム開発や設定の保存で利用されます。

**利用シーン例**: 複数の設定を1つの変数で管理、状態フラグの制御。

---

### 3. **ビットカウント（1のビット数を数える）**
ある数値に含まれる1ビットの数（ハミング重量）を数えることで、データ処理や圧縮に活用されます。

**利用シーン例**: エラーチェック、圧縮アルゴリズム、データ解析。

---

### 4. **ビット回転（ローテーション）**
ビットを回転させる操作は、暗号化やハッシュ関数で使われ、データの順序を効率的に操作できます。

**利用シーン例**: 暗号化アルゴリズム、ハッシュ関数、データシフト。

---

### 5. **符号反転（負数の計算）**
ビット反転と加算を使って、効率的に符号を反転させることが可能です。負の数の計算に活用されます。

**利用シーン例**: 数値の符号反転、負の数計算。




## まとめ
ちょっととっつきにくいビット演算ですが、10進数と同じようなことが2進数で行われていることがわかれば、理解しやすいと思います。ビット演算は、各ビット（桁）ごとに計算が行われるため、10進数での桁ごとの計算と同じような感覚で理解できます。偶数・奇数の判定や2の倍数の操作に優れている理由は、各ビットが2進数の桁に対応しているためです。
10進数に変換しない分高速に処理できるため、処理速度を突き詰めて行くと、ビット演算の理解は大切だなと勉強してみて思いました。誰かの参考になれば幸いです。