SOFTELメモ Developer's blog

会社概要 ブログ 調査依頼 社員募集 ...

【JavaScript】~~ で小数の切り捨て(ビット演算子 ~2つで0方向へ切り捨て)

問題

これ、なんですか。この、にょろにょろ。

var y = ~~(x/2);

答え

ビット否定演算子(Bitwise NOT Operator)を2つ~~使うと、小数の切り捨てができる。

>>> ~~(0.8)
0
>>> ~~(1)
1
>>> ~~(1.2)
1
>>> ~~(1.5)
1
>>> ~~(1.9)
1
>>> ~~(123.456)
123

便利なのが、0へ向かって切り捨ててくれるところ(絶対値で切り捨て)。

>>> ~~(-123.456)
-123
>>> ~~(-456.789)
-456

素直に正負を見て関数を使い分けるとこうなるところ。長くて嫌になりますね。

//xが数値の前提で
x = (x > 0)
	? Math.floor(x)
	: Math.ceil(x);

それが、~~なら、ほら!このとおり。

//xは何でもいいですよ♪
x = ~~x;

括弧をつけると、関数っぽいですね。

var y = ~~(x);

もうちょっと細かく

ECMA Scriptの仕様によると、ビット演算するときに、対象をToInt32で32ビット整数にする。

ToInt32の定義は以下のとおり。

9.5 ToInt32: (Signed 32 Bit Integer)

The operator ToInt32 converts its argument to one of 2^32 integer values in the range -2^31 through 2^31-1, inclusive. This operator functions as follows:

1. Call ToNumber on the input argument.
2. If Result(1) is NaN, +0, -0, +∞, or -∞, return +0.
3. Compute sign(Result(1)) * floor(abs(Result(1))).
 ↑これ。絶対値で切り捨てて、符号はそのまま。

4. Compute Result(3) modulo 2^32 ; that is, a finite integer value k of Number type with positive sign and less than 2^32 in magnitude such the mathematical difference of Result(3) and k is mathematically an integer multiple of 2^32 .
5. If Result(4) is greater than or equal to 2^31 , return Result(4)-2^32 , otherwise return Result(4).

他にも演算させてみるとこのようになる。

~~null;      // => 0
~~undefined; // => 0
~~0;         // => 0
~~{};        // => 0
~~[];        // => 0
~~(1/0);     // => 0
~~false;     // => 0
~~true;      // => 1
~~1.2543;    // => 1
~~4.9;       // => 4
~~(-2.999);  // => -2

参考

http://james.padolsey.com/javascript/double-bitwise-not/

9.5 ToInt32: (Signed 32 Bit Integer)

関連するメモ

コメント(5)

匿名 2012年4月23日 08:45

x = ~~x;

x |= 0;
と書けます

yoshimura 2012年4月23日 09:06

なるほど、変数に入っているときは、その手もありますね。
ビット演算子 |(OR)すると、ToInt32されて、相手が0だから値は変わらない。

エーテル水槽 2012年4月24日 05:24

32ビット整数にしちゃってるので、正しく切り捨てられる数には限界があったりするみたいです。

javascript:alert( ~~999999999.9999999999999 ); //1000000000
javascript:alert( ~~9999999999.9999999999999 ); //1410065408

なので、最大値を考慮して使わないと危険です。

匿名 2012年4月25日 01:03

Math.roundでいいんじゃ…

fflo 2012年5月6日 07:13

関数使う場合は parseInt ですね。