My開発メモ

ビット否定演算子(JavaScript)

ビット演算子のうちの ~(チルダ)について調べてみた。

整数の場合

まず、整数から。

最初

let a = 10;
a.toString(10) // "10"
a.toString(16) // "a" ==> 0x0a
a.toString(2) // "1010" ==> 0b00001010

16進数では 0a
2進数では 0000 1010
となる。

~(チルダ)

これをビット否定したのが、以下。

let b = ~a;
b.toString(10) // "-11"
b.toString(16) // -b ==> -0x0b ==> 0xf5
b.toString(2) // -1011 ==> -0b00001011 ==> 0b11110101

ビット否定というのは、各ビットを反転したもので、
~a は、2進数だと 1111 0101 となる。
16進数だと f5 である。

上記で、2進数で “-1011” というのは、どうやら 2の補数表現のようである。
つまり、0b00001011 + 0b11110101 = 0b00000000 となるので、
“-0b00001011” は “0b11110101” の別の表現だということだろう。

同様に、16進数で “-b” というのは、”0x0b” ということで、
0x0b + 0xf5 = 0x00 となるので、
0xf5 は -0x0b と表現できる。

~~(チルダチルダ)

これをさらにビット否定すると。

let c = ~~a;
c.toString(10) // "10"
c.toString(16) // "a" ==> 0x0a
c.toString(2) // "1010" ==> 0b00001010

もとに戻った。

マイナス整数の場合

もとの形

let a = -10;
a.toString(10) // "-10"
a.toString(16) // "-a" ==> -0x0a ==> 0xf6
a.toString(2) // "-1010" ==> -0b00001010 ==> 0b11110110

“-a” というのは、-0x0a ということで、0xf6 ということである。
“-1010” というのは、-0b00001010 ということで、0x11110110 のことである。

~(チルダ)

let b = ~a;
b.toString(10) // "9"
b.toString(16) // "9" ==> 0x09
b.toString(2) // "1001" ==> 0b00001001

各ビットを反転すると、上のようになる。

~~(チルダチルダ)

let c = ~~a;
c.toString(10) // "-10"
c.toString(16) // "-a"
c.toString(2) // "-1010"

もとに戻った。

小数点の場合

小数点の数は、64ビットの浮動小数点数で表現される(もともと整数の場合も同じ)。

わかりやすくするために 2.25 という数を使う。

もとの形

let a = 2.25
a.toString(10) // "2.25"
a.toString(16) // "2.4" ==> 0x02 40
a.toString(2) // "10.01" ==> 0b00000010 01000000

2は 16進数だと 0x02、2進数だと 0b00000010 である。
小数部は 16進数だと 40、2進数だと 0100 0000 となる。
0.25 は 1 / 2の2乗

0.5 ==> 1 / 2の1乗 ==> 1000 0000
0.25 ==> 1 / 2の2乗 ==> 0100 0000
0.125 ==> 1 / 2の3乗 ==> 0010 0000

~(チルダ)の場合

let b = ~a
b.toString(10) // "-3"
b.toString(16) // "-3" ==> -(0x03) ==> 0xfd
b.toString(2) // "-11" ==> -(0000 0011) ==> 1111 1101

JavaScriptでは、ビット演算するときは、小数点のある数は 32ビット整数に
変換される(らしい)。
そして、小数部は 0に近い数に丸められる。

2.25 の場合は まず32ビット整数に変換されてから、NOT演算されるのだろう。

2.25 ==> 0000 0010 | 0100 0000 ==> 0000 0010 と小数部が切り捨てられ、
ビットごとに反転されるから、1111 1101 となる。
これの 2の補数表現は 0000 0011 である。

~~(チルダチルダ)の場合

let c = ~~a
c.toString(10) // "2"
c.toString(16) // "2" ==> 0x02
c.toString(2) // "10" ==> 0b 0000 0010

bを反転した数になっている。これは もとの数の小数部を切り捨てたのと同じである。

マイナスの小数点の場合

-2.25 で考えてみる。

もとの形

let a = -2.25
a.toString(10) // "-2.25"
a.toString(16) // "-2.4" ==> -(02.40) ==> fe.c0
a.toString(2) // "-10.01" ==> -(0000 0010 . 0100 0000)
                          ==> 1111 1110 . 1100 0000

a.toString(2) で、-2.25 の2進数を表示させてみると、”-10.01″ となる。
これは 2の補数表現で、”-(0000 0010 . 0100)” ということなので、
2の補数をもとめると、”1111 1110 . 1100″ となる。

~(チルダ)の場合

let b = ~a
b.toString(10) // "1"
b.toString(16) // "1" ==> 01
b.toString(2) // "1" ==> 0000 0001

ビット演算されると 小数点以下は0に丸められる。
整数部を反転すると、”1111 1110″ => “0000 0001” となる。

~~(チルダチルダ)とすると。

let c = ~~a
c.toString(10) // "-2"
c.toString(16) // "-2" ==> -(02) ==> fe
c.toString(2) // "-10" ==> -(0000 0010) ==> 1111 1110

2進数だと、”0000 0001″ を各ビットで反転するから、”1111 1110″となる。
これを2の補数表現にすると、”0000 0010″ となり、”-10″ となる。
これは、10進数では “-2″ なので、”-2.25″ の小数部を切り捨てたものとなる。

カテゴリー: JavaScript, memo

タグ: ~, ~~, bit演算, NOT演算, チルダ, ビット反転, ビット演算, 小数点以下切り捨て

カウント: 134