結論
暗算の苦手な人は、irbが電卓として便利に役立ちます。
過程
(一応)組込みプログラマーとして仕事をしていると、10進数から16進数への変換、16進数から10進数への変換というのをしばしば行う。
レジスタに値を入れたり、単体テストのためにそれっぽい値を用意したり。
レジスタへの値の設定、確認などは大体ビット単位で意味が決まっているので、2進数を使ったりもする。
慣れていけば頭のなかで計算も可能なのだけれど、算数が元から得意じゃなく、暗算も苦手な自分はいつも関数電卓の変換機能を使っていた。
これが実に面倒くさい。
rubyの関数の挙動を調べるためにirbを使っていたところ、これで計算、変換したらよくね? ということに気付いた。
環境
自分は家でも仕事でもWindowsなので、Cygwin + ruby 1.9.3を使用している。
irbのバージョンは 0.9.6(09/06/30)。
ずいぶん古く感じるけれど、基本的な機能しか使用しないので問題ないはず。
例
10進数を16進数に変換
例1 10進数8111を16進数に変換する
irb(main):001:0> 8111.to_s(16)
=> "1faf"
例2 10進数8111を0付き8桁の16進数に変換する
irb(main):002:0> format("%08x", 8111)
=> "00001faf"
数値クラスのものを16進数に変換する方法、整え方は様々。
細かい話は下記リンクを見れば概ね問題なし。
自分はsprintfという字面があまり好きではないので、formatをよく使う。好みの問題。
16進数を10進数に変換
例1 16進数0xdeadbeefを10進数に変換する
irb(main):003:0> 0xdeadbeef
=> 3735928559
何もしなくてもいい。irbの標準出力は自動で10進数に変換してくれる。
あえて文字列するなら、下記のように
例2 16進数の0xfacefeedを10進数の文字列オブジェクトに変換する
irb(main):004:0> 0xfacefeed.to_s(10)
=> "4207869677"
2進数を10進数に変換
例1 2進数11010001を10進数に変換する
irb(main):006:0> 0b11010001
=> 209
これも前述同様に変換は不要。基本、10進数への変換には値を入力してエンターキーを押すだけで、何もする必要がない。
ちょっとしたビット演算、計算をする
例1 0xbabaを2ビットシフトして8桁の16進数を求める
irb(main):007:0> format("%08x", (0xbaba << 2))
=> "0002eae8"
formatを入力するのが手間なら、単純に下記でかまわない。あとから0を自分で補う。
irb(main):009:0> (0xbaba << 2).to_s(16)
=> "2eae8"
例2 0xddcdの7ビット目を反転した16進数を求める
irb(main):011:0> (0xddcd ^ (1 << 6)).to_s(16)
=> "dd8d"
「7ビット目の反転」に用いるXORの対象は「1を6ビット左シフトしたもの」であることに注意。頭が回らない時なら、素直にこういうこともできる。
irb(main):012:0> (0xddcd ^ 0b01000000).to_s(16)
=> "dd8d"
数え間違えるケースがあるので、こっちはこっちで注意が必要ではある。
例3 10進数842の16ビット幅における2の補数を16進数で求める
irb(main):013:0> ((~842 & 0xffff) + 1)
=> 64694
irb(main):014:0> ((~842 & 0xffff) + 1).to_s(16)
=> "fcb6"
もしかすると補数の求め方には、もっと便利な関数が存在するのかもしれない。
ここでは情報処理の基礎に則って、「反転したのち+1」というオーソドックスな計算方法で求めている。
例3のような処理はいつ役立つのか。具体的に言うと18ビット幅の分解能を持つデルタシグマ式のADCをバイポーラ設定で用いるときなどに役立つ。こういった値を求めるのはなかなか手間なので、irbの真価が発揮されるように感じる。
例4 10進数8675の18ビット幅における2の補数を16進数で求める
irb(main):018:0> ((~8675 & 0x3ffff) + 1).to_s(16)
=> "3de1c"
こういう時は、怖いので念のため検算をすることがある。精神安定剤に近い。
irb(main):022:0> a = ((~8675 & 0x3ffff) + 1).to_s(16)
=> "3de1d"
irb(main):023:0> (a.hex + 8675).to_s(16)
=> "40000"
2の補数なので、足しあわせて18bit幅の範囲ですべて0になれば問題ない。
上記のように変数を使うのも有効的だ。.hexは16進数の文字列オブジェクトを数値オブジェクトに変換している。
複雑な変換、あるいは計算には変数を用いるのは効果的だが、対話型の場合どこに何が入っているのか、どの変数が何を意味しているのか忘れることもあるので、やり過ぎに注意。
以上、チラ裏。