この前に、popcountを計算するGemを作りましたが、やりたいことが増えてきたのでbit_count
という名前も合わなくなるということで、新たなGemにすることにしました。題して、bit_utils
としています。
3つの機能
名前的には汎用とできるようになったところで、現状の機能は3つを用意しています。ビット単位のAND/ORやシフトについては、通常のRubyの演算でまかなえるので、特段用意はしていません。
popcount
元になったbit_counter
の機能を引き継いで、1になっているビットの数を数える機能があります。ただし、前のところでも書いたように、負の数では1のビットが無限個になってしまうので、0の個数を数えてマイナスで返します。
require 'bit_utils'
p BitUtils.popcount(0) # => 0
p BitUtils.popcount(1) # => 1
p BitUtils.popcount(65535) # => 16
p BitUtils.popcount(-2) # => -1
trailing_zeros
Rubyの整数には「全体幅」という概念が存在しないのですが、その代わりに有意なビット数を返すbit_length
メソッドがあります。そして、多くのCPUでは固定幅の整数に対して、「上のビットから0が続く数」「下のビットから0が続く数」を数えるような命令があります。
ということで、Rubyでも「下のビットから0が続く数」を知りたくなって作ることにしました。なお、0の時は-1を返すようにしてあります。マイナスの時も特段のハンドリングは必要ないので、適切な値を返します。
require 'bit_utils'
p BitUtils.trailing_zeros(0) # => -1
p BitUtils.trailing_zeros(1) # => 0
p BitUtils.trailing_zeros(2) # => 1
p BitUtils.trailing_zeros(1000) # => 3
p BitUtils.trailing_zeros(65536) # => 16
p BitUtils.popcount(-2) # => 1
each_bit
整数をビットの集合として扱うような場合に、その要素ごとに繰り返し実行するようなメソッドです。なお、yield
の順序は不定で、また負の数の時は例外を投げます。
他のeach
系メソッドと同様、ブロックを渡さない場合にはEnumerator
を返すので、.to_a
など好きなように使えます。
require 'bit_utils'
BitUtils.each_bit(0) { |b| p b } # しかし、なにもおこらなかった!
BitUtils.each_bit(1) { |b| p b } # => 0
BitUtils.each_bit(63).to_a # => 0から7まで入った配列(順番は不定)
コア拡張
require 'bit_utils/core_ext'
とすると、Integer
に上3つのメソッドが生えます。require 'bit_utils/core_ext/each_bit'
のようにして、一部だけ組み込むことも可能です。