Javascriptのシフト演算の挙動

Javascriptで符号なし32ビット整数っぽいシフト演算をさせたかったときにハマったのでメモ.

必要な前提知識
  • コンピュータ上での数値の表し方(浮動小数点,符号付き整数,2進数[16進数]の計算とか)
  • シフト演算(算術シフト,論理シフト)

まずは例から

0x80000000         //1.  2147483648 (231)
0x100000000        //2.  4294967296 (232)
0x800000FF >> 1    //3. -1073741697 (0xC000007F)
0x800000FF >>> 1   //4.  1073741951 (0x4000007F)
0x5FFFFFFF << 1    //5. -1073741826 (0xBFFFFFFE)
0x5FFFFFFF << 2    //6.  2147483644 (0x7FFFFFFC)
0x80000000 <<< 1   //7.  SyntaxError: Unexpected token <

そもそもJavascriptは通常数値を64ビットの浮動小数点で表しています.
ただし,シフト演算をすると32ビットの符号なしあるいは符号付き整数にキャストされる.
また,右シフトには算術シフトと論理シフト用の演算子がそれぞれありますが,左シフトには論理シフト(?)しかない.

  1. は16進数0x80000000を出力しているだけなので231乗の2147483648が出力される
  2. は32ビットをオーバーしていますが,前述のとおり64ビット浮動小数点で数値は表されるので231-1以上の数値を扱えます
  3. では0x800000FFを1ビット右シフトしていますが,この右シフトは算術シフトなので,0x800000FF→0xC000007Fとなり-1073741697となる
  4. では0x800000FFを1ビット右シフトしていますが,このシフトは論理シフトなので左ビットには0がしきつめられるので0x800000FF→0x4000007Fとなり結果は1073741951となる
  5. は1ビット左シフトしていますがこれは論理シフトなので,0x5FFFFFFF→0xBFFFFFFEとなりこれは32ビット整数では-1073741826となる.
  6. 5をさらに1ビット左論理シフトするので0x5FFFFFFF→0x7FFFFFFCとなりこれは32ビット整数で2147483644となる
  7. は左シフトには論理シフトしかないのでそもそも演算できなくてエラーになっている
まとめ
  • Javascriptでは通常では数値を64ビット浮動小数点で表す
  • ただしシフト演算時は算術右シフト(>>)および左シフト(<<)の場合符号付き32ビット整数,論理右シフト(>>>)の場合は符号なし32ビット整数にキャストされて計算される
結論

というわけで32bit符号なし整数っぽい挙動をさせたければこんな感じにすればよさそう.

// 32ビット符号なし整数っぽく数値をシフト演算する
0xFFFFFFFF                 //294967295
0xFFFFFFFF >>> 1           //1ビット右シフト
//1ビット左シフト(1ビット左シフトしただけでは負数なので,0ビット右シフトして符号無し化する
(0xFFFFFFFF << 1) >>> 0