5
2

1 << 31と2 ** 31は同じ値ですか?いいえ違います。JavaScriptではね

Last updated at Posted at 2024-06-07

TL;DR;

JavaScriptにおいて乗算のかわりにbit shiftをつかってもは同じにならないことがある
これはJavaScriptの数値が64bit doubleであることに起因している
計算するときにbit shiftのことを理解していないのであれば、これを使用してはいけない

bit shiftとか計算とかのおさらい

計算機では内部的には電気信号を操作して、数値や計算を実装しています
回路の話とかCPUだとかの話とかはおいておいて内部的に2進数で処理しています
これを操作するためにbit shiftという演算があります

0b00000001 << 1
2 (0b00000010)
0b00000001 << 2
4 (0b00000100)

となります

bit shiftではどこまで保証されているのか、答は31bit

bit shiftはだいたい2の指数計算と一緒です
ただしそれは桁が保証されている範囲となります
計算の結果は以下のとおりとなります

2 ** 0 -> 1
2 ** 1 -> 2
2 ** 2 -> 4
2 ** 3 -> 8
2 ** 4 -> 16

これくらいの計算であればbit shiftでも同様にできます

1 << 0 -> 1
1 << 1 -> 2
1 << 2 -> 4
1 << 3 -> 8
1 << 4 -> 16

よくわからない人はたとえば
0b0001を左に3つ移動したら0b1000になりますが、これは8です、というのを理解しておいてください

そしてこれはどこまで確実でしょうか?
答えは31bit分までです

2 ** 30
> 1073741824
1 << 30
> 1073741824
2 ** 31
> 2147483648
1 << 31
> -2147483648 

bit shift演算では32bit(符号あり)整数として処理されます
JavaScriptのNumberの精度は仮数部で52bitあるのもあって、乗算では普通に計算してくれます
おそらくは仮数部分が52bitなので、bit shift演算が有効なのは32bitまでで64bitではないのだな、と思いました(実際、52bitで演算するのも変な話?ですし、64bitだと52bit以降はよくわからない結果になることはわかる)
32bitでbit shiftを扱うのがIEEE 754の規定なのかどうかは知らないので、ご存知の方はおしえてください

じゃあ32桁分shiftしたらどうなるの?

1 << 31
> -2147483648
1 << 32
> 1

一見rotateしてますが、以下でおかしなことがおこります

2 << 30
> -2147483648
2 << 31
> 0
2 << 32
> 2
2 << 33
> 4

おい、ちょっと待て。
普通にbit rotateしてたら2 << 31は0ではなく1なのではないか?
つまりbit rotateしてるのではなく、31までは普通にbit shiftしてあふれた分はおとしてるけど、32のときはbit shift 0と同様にあつかっているのではないか?

どうしてもbit shiftしたい人むけ

bigintつかってください

2n << 31n
4294967296n
2n << 32n
8589934592n

乗算する分には大丈夫だよね?

Number.MAX_SAFE_INTEGER以下の数値ならね
ちなみにこれは(2^53 - 1)です
64bit doubleの仮数の限界です
これを越えて正しく計算したい場合はBigintを使用してください

≈
Number.MAX_SAFE_INTEGER
9007199254740991

2 ** 53
9007199254740992
2n ** 53n
9007199254740992n
2 ** 54
18014398509481984
2n ** 54n
18014398509481984n
2 ** 55
36028797018963970
2n ** 55n
36028797018963968n //ずれた

まとめ

Numberを安易にbit shiftしてはいけない
bit shiftが32bitで行われていることや32桁以上のshiftが循環するのはchromeだけの仕様かもしれないので、ちがっていたらなんか書いてください
(調べるのがそろそろ苦痛なので、やり過ぎると上司におこられるので)

感想

なんでこんな記事が誕生したかというとBigEndienなUint8ArrayをNumberに変換しようとしてbit shiftつかっておしゃれなコード書こうとしたら数値がおかしくなったのが原因です
ことと次第を調べていたら、こんなことになっていました

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2