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つかっておしゃれなコード書こうとしたら数値がおかしくなったのが原因です
ことと次第を調べていたら、こんなことになっていました