符号付き vs 符号なし右シフト

>>(符号を保持する算術シフト)と>>>(ゼロで埋める論理シフト)の重要な違いを、負の数について理解します。

Shift Operations

詳細な説明

算術(>>)vs 論理(>>>)右シフト

JavaScriptは両方のシフト演算子を提供し、負の数で区別が重要になります。

算術右シフト(>>)

空いた上位ビットを符号ビット(最左ビット)で埋めます。負の数の符号を保持します:

  -16(32ビット) = 11111111111111111111111111110000
  -16 >> 1       = 11111111111111111111111111111000  (-8)
  -16 >> 2       = 11111111111111111111111111111100  (-4)

数学的な結果はfloor(n / 2^shift)で、負の無限大に向かって丸めます。

論理右シフト(>>>)

符号ビットに関係なく、常に空いた上位ビットを0で埋めます:

  -16(32ビット) = 11111111111111111111111111110000
  -16 >>> 1      = 01111111111111111111111111111000  (2147483640)
  -16 >>> 2      = 00111111111111111111111111111100  (1073741820)

いつ違いが生じるか?

正の数では両演算子は同一の結果:

  16 >> 1  = 8
  16 >>> 1 = 8

違いは負の数または32ビット値を符号なしとして扱う必要がある場合のみ現れます。

JavaScriptに固有の動作

JavaScriptの数値は64ビット浮動小数点ですが、ビット演算子は32ビット整数で動作します。>>>演算子はJavaScriptで符号付き32ビット整数を符号なしに変換する唯一の方法です:

(-1 >>> 0).toString(16)   // "ffffffff" (4294967295)
(-1 >> 0).toString(16)    // "-1"

一般的な使用例:符号なしへの変換

function toUint32(n) {
  return n >>> 0;
}

TypedArray操作、ハッシュ関数、バイナリプロトコル実装で頻繁に使用されます。

ユースケース

JavaScriptのハッシュ関数実装では、値が符号なし32ビット範囲に収まることを保証するために`>>> 0`を使用します。例えばMurmurHash3やCRC32は符号なし演算を必要とします。`>>> 0`がなければ、JavaScriptの符号付き32ビットビット演算セマンティクスにより中間値が負になり、不正なハッシュ値が生成されます。`(hash * prime) >>> 0`パターンはJavaScriptハッシュライブラリの標準です。

試してみる — Bitwise Calculator

フルツールを開く