ビット否定演算子(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