はじめに
ビット演算を利用するケースでは unsigned を期待することが多いと思うのですが JavaScript ではその期待は捨てたほうが良いです。(ただし >>> 演算子を除く) ここではその具体例と対策、簡単な説明をしていきたいと思います。
具体例
ではさっそく、例として
0x12345678 ^ 0xFFFFFFFF
を見てみましょう。 (なぜ 32-bit かというと JavaScript のビット演算は 32-bit で行われるからです。)
0x12345678 は 2 進数表記にすると
0001 0010 0011 0100 0101 0110 0111 1000 になります。
これを XOR 0xFFFFFFFF で反転させるのですから、
下記のように計算して期待する値は 0xEDCBA987(=3,989,547,399) となるはずです。
0001 0010 0011 0100 0101 0110 0111 1000
^ 1111 1111 1111 1111 1111 1111 1111 1111
-----------------------------------------
1110 1101 1100 1011 1010 1001 1000 0111
しかし、実際に実行してみると
-305419897
という値になります。そうです。signed なんです。
それだけならまだ分かりにくいだけで良いかも知れません。
しかし、以下の例コードを実行してみてください。
(0x12345678 ^ 0xFFFFFFFF) === 0xEDCBA987 // false
これが通らないのはおかしいと思いませんか?
(とは言っても、上記を展開すると -305419897 === 3989547399 となるので false となるのが当然なんですが。)
対策
上記の例を、あまり変更せずに通るようにするにはどうしたらよいかというと
((0x12345678 ^ 0xFFFFFFFF) >>> 0) === 0xEDCBA987 // true
とするだけです。
>>> 演算子は結果を unsigned で返すため、簡単な型変換に利用することができます。
説明
ここまで読んでも
(0x12345678 ^ 0xFFFFFFFF) === 0xEDCBA987
は両方とも 16 進表記にすると 0xEDCBA987 なのに何故一致しないのか疑問に思う人もいるかもしれません。
ECMA-262 5.1 によると、ビット演算子は以下のような型変換を行うとされています。
| 演算子 | 参照した章 | 結果の型 |
|---|---|---|
| ~ | 11.4.8 Bitwise NOT Operator ( ~ ) | signed 32-bit integer |
| << | 11.7.1 The Left Shift Operator ( << ) | signed 32-bit integer |
| >> | 11.7.2 The Signed Right Shift Operator ( >> ) | signed 32-bit integer |
| >>> | 11.7.3 The Unsigned Right Shift Operator ( >>> ) | unsigned 32-bit integer |
| &, ^, | | 11.10 Binary Bitwise Operators | signed 32 bit integer |
また、JavaScript の数値は IEEE 754 であるとされています。( "4.3.19 Number value" より)
つまり、0x12345678 ^ 0xFFFFFFFF の -305419897 という値は 32-bit integer ではなく、
ビット演算子によって signed 32-bit integer に変換されたのち IEEE 754 形式にされるため、
0xEDCBA987 = 3989547399 の IEEE 754 形式と一致しないというわけです。
: document <Liked it!