0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

irbにおける"~(チルダ)"によるビット反転

Posted at
mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0> 

#はじめに
ことの発端は上記。何をしているかを簡単に説明すると

1行目$ irb実行
2行目> "irb完全に理解した"という文字列に対しlengthメソッドを実行
=> "irb完全に理解した"の文字数である10が返り値として出力
4行目> ~を入力
5行目* "irb完全に理解した"という文字列に対しlengthメソッドを実行
=> -11が返り値として出力される

なぜ ~ を入力したのかはこの際問題ではない(実際ただのタイプミスだ)。
ここで生じた疑問は以下の3つである。
疑問1, ~ とはなんなのか?
疑問2, プロンプトの末尾が * になるのはなぜか?
疑問3, なぜ1,2の状態において返り値が変わるのか?
###疑問1について
~(チルダ)はIntagerクラスのオブジェクト(整数)に対してビット単位で演算を行うための演算子(ビット演算子)の一種である。
その役割はビット反転であり、~a(a = Integer)と記述することで「aを2進数に変換した際の各桁の数字(0或いは1)をそれぞれ入れ替える(0ならば1に, 1ならば0に)」というもの。
例えば、10進数における25は2進数では11001と表現されるが、それを~(チルダ)でビット反転させると00110(10進数における6)となる。
###疑問2について
プロンプトの末尾が * になっているのは、irbが「コードが完結していない」と判断しているからである。例えば

mypc:~ current$ irb
irb(main):001:0> hoge = "ホゲ"
=> "ホゲ"
irb(main):002:0> hoge =
irb(main):003:0* "ホゲ"
=> "ホゲ"

上記2~3行目の処理と4~6行目の処理は全く同一のものであると言える。
冒頭の5行目も、演算子である ~ を記述しただけの状態で改行したため、
irb側が「まだコードは完結しておらず、次の行には ~ によってビット反転される何かしらの値が入力されるはずだ」と認識したからであると思われる。

###疑問3について
疑問1,2の解答を踏まえ、もう一度冒頭のコードを確認する

mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0> 

10進数の10を2進数に変換すると1010。
これに ~ をあてがうと、0101となる。
0101を10進数に変換すると、5となる。
この結果、返り値は5に––––––––––––––––––––––––––



あれ?

#新たな疑問 ~ちゃぶ台返し~
      ありえない事態が起こったなら
        疑うべきは2つだけだ

       前提条件が間違っているか
    それともあんたの頭がいかれちまったか

                    〜狡噛慎也〜

そもそもの前提として私の頭はいかれていない。
これすら間違っているなら、最早この記事は出来損ないのドグラ・マグラである。
なので残る二つの前提条件、即ち疑問1,2の解答を疑うしかない。
疑うしかない。が、ともかくもまずは、情報を改めて整理しようと思う。
##~(チルダ)について
~(チルダ)について、改めて調べ直した。
Google師父に「ビット演算子 ~ チルダ」と入力し検索をかける。
するとC#におけるチルダについて質問しているページがヒットした。他言語に関するページではあるが、共通する部分もあるだろう。

曰く、「これは、ビットごとの否定とも呼ばれる ビットごとの補数 演算子です。」とのこと。
ビットごとの否定...。これは疑問1でも言及した「ビット反転」と同義と捉えて問題ないだろう。
気になるのはビットごとの補数である。ご丁寧にリンクが貼られているのでそちらを見に行く。

"The ~ operator produces a bitwise complement of its operand by reversing each bit:"
「~オペレータは、各ビットを反転させることによって、そのオペランドのビット単位の補数を生成します。」
※Google翻訳
未履修言語の話なのであまり適当なことは言えないが、ニュアンスは概ね同じのように思える。
しかしまた登場した。補数。...補数。補数???補数ってなんだったっけか。
とにかく、「~aとすることでaの補数を生成する」ということらしいが...
##補数について
いきなり数学の授業である。Wikipediaを見れば全部書いてあるが、

「足すことによって桁上がりする数」
というのが補数の定義らしい。
例えば10進法における45に対する10の補数は55であり、
2進法における1101101に対する2の補数は10010である。

##~aをaに対する補数と仮定する
仮定の上で、再三にはなるが冒頭のコードを確認しよう

mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0> 

10進数10を2進数に変換すると1010
10進数-11を2進数に変換すると11111111111111111111111111110101
...下4桁、明らかに怪しい。
が、とりあえずいくつか組み合わせを試して検証してみる。
スクリーンショット 2020-07-06 7.48.52.png
1がいっぱいである。そしてどうやら
10進数において、入力値(~は除く)+出力値=全て-1に
2進数において、入力値(~は除く)+出力値=全て11111111111111111111111111111111
になるらしいということがわかった。
#ひとまず結論
つまり、irbにおいて~(チルダ)は、11111111111111111111111111111111を基数とした上で
入力された値を2進数変換した値に対する11111111111111111111111111111111の補数を返り値として出力していたのである。
#11111111111111111111111111111111ってどこからきた?
状況は理解した。が原因がわからない。
11111111111111111111111111111111
32桁に1が収まっている。32...?
32ビットとか?関係ある?

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?