へっぽこ社会人4年生がプログラミングを頑張る

へっぽこ社会人4年目がプログラミング系統を中心に書きたいことをつらつらと書きます

JavaScriptでの2重のビットNOT演算による小数点の切り捨て

JavaScriptを使って、Canvas上にお絵かきをするツールを作ろうと思い、 情報収集をしていた時に、こんなコードが目に飛び込んできました。

var rect = evt.target.getBoundingClientRect();
var x = ~~(evt.clientX - rect.left);
var y = ~~(evt.clientY - rect.top);

evt.clientX - rect.leftevt.clientY - rect.topは座標の計算をしているのは分かるんだけれど、 ~~で一体何をしているか分かりませんでした。

~演算子がビットを反転させる演算子で、それが2つあるから、2回ビットが反転、つまり元に戻ることになります。 なんでわざわざそんなことをしているか、理解出来ませんでした。 e.clientXe.clientYが実数になりうることが明記されているページ(どこだったか忘れた)を見て、 もしかして、小数点以下を四捨五入でもさせているのかなと思い、ブラウザのコンソールを開き、 実数値に2重のビットNOTを適用して確認しました。 結果的に、小数点を切り捨てているということが分かり、謎だった2重のビットNOTがやっと理解出来ました。

疑問点

そもそも、ビット演算って、整数型に対しての演算じゃなかったのかと、JavaScriptのビット演算について軽く調べました。 どうやら、JavaScriptで数値を整数化するために、ビット演算を使うという小技があるみたいです。 ただ、巨大な数値に対して使うと意図しない挙動をすることがあらしいです。 (正直、個人的にはビット演算はCでは整数型の演算だから、実数に対して使うのはどうなのかなと思いますが...)

JavaScript以外の言語でも同じことできるのかなと思ったので、実際に試してみました。 (Cはエラー出ることが既知なので、省略しました)

a=1.1の時のビット演算
言語 ~a ~~a
JavaScript -2 1
Perl 18446744073709551614 1
PHP -2 1
Python 型エラー 型エラー

PerlPHPでも同じこと出来て驚きました。 ただ、Perl~$aの結果だけPHP, JavaScriptと違うのが疑問でした。 Pythonは型エラー吐いて、なんか少しだけ安心しました。(Python3も同じ結果)

2重のビット反転で数値を整数化する方法もあるということを知れて、勉強になりました。 ただ、個人的には、ビット演算じゃなくて、整数に変換する関数(JavaScriptだったら、parseInt()Math.floor())を使った方が良いんじゃないかなと思いました。 (そっちの方が、可読性も良さそう)

参考文献