JavaScript/ビット演算

出典: フリー教科書『ウィキブックス(Wikibooks)』
JavaScript > ビット演算

ビット演算[編集]

Wikipedia
Wikipedia
ウィキペディアビット演算の記事があります。


JavaScriptはビット(二値)演算をサポートしています。

  • & ビットごとのAND(論理積)
  • | ビットごとのOR(論理和)
  • ^ ビットごとの排他的 OR(排他的論理和)
  • << 左シフト
  • >> 右シフト
  • >>> 符号なし右シフト演算子
  • ~ 1の補数を得る

また、次の様な複合代入演算子も用意されている

  • &= ビットごとのAND演算をして代入
  • |= ビットごとのOR演算をして代入
  • ^= ビットごとの排他的ORをして代入
  • >>= 右シフトして代入
  • <<= 左シフトして代入
  • >>>= 符号なし右シフト演算子

JavaScriptでビット演算を行ううえで注意すべきことは、ビット演算を行う前にNumber型の値が32ビット整数に強制変換されるということです。

これは、JavaScriptの数値型は倍精度浮動小数点型(64ビットFloat)で、仮数部が53ビットであり、それ以下で最大の2の冪乗が32ビットという理由からです。

コード例
// AND演算子(&)
console.log(`AND演算子(&):
  0b101
& 0b011
-------
  0b${(0b101 & 0b011).toString(2).padStart(3, '0')}
`);

// OR演算子(|)
console.log(`OR演算子(|):
  0b101
| 0b011
-------
  0b${(0b101 | 0b011).toString(2).padStart(3, '0')}
`);

// XOR演算子(^)
console.log(`XOR演算子(^):
  0b101
^ 0b011
-------
  0b${(0b101 ^ 0b011).toString(2).padStart(3, '0')}
`);

// NOT演算子(~)
console.log(`NOT演算子(~):
  ~0b101
-------
0b${(~0b101 & 0b111).toString(2).padStart(3, '0')}
`);

// 左シフト演算子(<<)
console.log(`左シフト演算子(<<):
  0b101
<<    1
-------
  0b${(0b101 << 1).toString(2).padStart(3, '0')}
`);

// 右シフト演算子(>>)
console.log(`右シフト演算子(>>):
  0b101
>>    1
-------
  0b${(0b101 >> 1).toString(2).padStart(3, '0')}
`);

// ゼロ埋め右シフト演算子(>>>)
console.log(`ゼロ埋め右シフト演算子(>>>):
  0b101
>>>   1
-------
  0b${(0b101 >>> 1).toString(2).padStart(3, '0')}
`);
実行結果
AND演算子(&):
  0b101
& 0b011
-------
  0b001

OR演算子(|):
  0b101
| 0b011
-------
  0b111

XOR演算子(^):
  0b101
^ 0b011
-------
  0b110

NOT演算子(~):
  ~0b101
-------
0b010

左シフト演算子(<<):
  0b101
<<    1
-------
  0b1010

右シフト演算子(>>):
  0b101
>>    1
-------
  0b010

ゼロ埋め右シフト演算子(>>>):
  0b101
>>>   1
-------
  0b010

2進法の表記法[編集]

JavaScriptでは、2進数を表す際には数値の前に 0b を付けます。例えば、0b1010 は10進数の10に対応します。

const binaryNumber = 0b1010; // 2進数の10を表す
console.log(binaryNumber); // 出力: 10

補数[編集]

JavaScriptにおいて、負の整数は2の補数形式で表現されます。例えば、-5はビット反転(NOT演算子)を行い、1を加えた結果として表現されます。

const negativeNumber = -5;
console.log(negativeNumber.toString(2)); // 出力: "-101"

ビットシフト[編集]

ビットシフト演算子は、ビット列を左または右に移動させます。左シフト演算子 << は数値を左にシフトし、右側にゼロを追加します。右シフト演算子 >> は数値を右にシフトしますが、符号ビットによって値を埋めます。

const number = 5; // 0b101
const leftShiftedNumber = number << 1; // 0b1010 (10)
const rightShiftedNumber = number >> 1; // 0b10 (2)
console.log(leftShiftedNumber, rightShiftedNumber);

論理演算[編集]

JavaScriptには論理演算子 AND (&)、OR (|)、XOR (^)、NOT (~) があります。これらの演算子は、対応するビットごとの論理演算を実行します。

const a = 0b1010; // 10
const b = 0b1100; // 12
console.log(a & b); // AND演算: 0b1000 (8)
console.log(a | b); // OR演算: 0b1110 (14)
console.log(a ^ b); // XOR演算: 0b0110 (6)
console.log(~a); // NOT演算: 0b11111111111111111111111111110101 (-11)

これらの演算子は、ビットごとの操作を行うため、ビットマスクやビット操作を行う際に役立ちます。

世界最初のコンピューターは二進法でない!?

世界最初のコンピューターとして現在知られているENIAC(エニアック)は、10進数(3増し符号)での計算方法を行っています。 しかし、現在の計算機では2進数で計算を行っています。 この変化は実際にかなり早い時期におこっており、1946年のENIACの登場の後に、2進数での設計の方が回路構成が単純になるという論文が登場しており、後継の1951年のEDVAC(エドバック)では既に2進数での計算に移っています。


イディオム[編集]

以下は、ビット演算のイディオムを1つのソースコードにまとめ、コメントで解説した例です。

/**
 * ビットフラグの操作
 * @constant {number} FLAG_A - フラグAを表すビットパターン
 * @constant {number} FLAG_B - フラグBを表すビットパターン
 * @constant {number} FLAG_C - フラグCを表すビットパターン
 */
const FLAG_A = 1 << 0; // 0b0001
const FLAG_B = 1 << 1; // 0b0010
const FLAG_C = 1 << 2; // 0b0100

/**
 * ビットがセットされた数をカウントする関数
 * @param {number} number - カウントする数
 * @returns {number} セットされたビット数
 */
function countSetBits(number) {
  let count = 0;
  while (number) {
    count += number & 1;
    number >>= 1;
  }
  return count;
}

/**
 * ビットマスクの使用
 * @constant {number} MASK_CLEAR_BIT_3 - ビット3をクリアするマスク
 * @constant {number} MASK_SET_BIT_4 - ビット4をセットするマスク
 */
const MASK_CLEAR_BIT_3 = ~(1 << 2); // ビット3をクリアするマスク
const MASK_SET_BIT_4 = 1 << 3; // ビット4をセットするマスク

// ビットフラグの操作
let options = 0;
options |= FLAG_A; // フラグAをセットする
options |= FLAG_B; // フラグBをセットする

console.log("オプション:", options.toString(2)); // オプション: 0b0011

// ビットがセットされた数をカウント
console.log("ビットがセットされた数:", countSetBits(0b101010)); // ビットがセットされた数: 3

// ビットマスクの使用
let data = 0b1101; // 0b1101
data &= MASK_CLEAR_BIT_3; // ビット3をクリアする
data |= MASK_SET_BIT_4; // ビット4をセットする

console.log("ビットマスク適用後のデータ:", data.toString(2)); // ビットマスク適用後のデータ: 0b1111

// ビット反転
const number = 0b1010; // 0b1010
const flippedNumber = ~number; // ビットの反転
console.log("ビット反転:", flippedNumber.toString(2)); // ビット反転: -0b1011

このコードは、ビットフラグの操作、ビットカウント、ビットマスクの使用、そしてビット反転という4つのビット演算のイディオムを示しています。それぞれの手法は、ビット演算を行う際に非常に便利であり、効率的なコードを書くための重要なツールとなります。