5

I have a variable and want to take a range of bits from that variable. I want the CLEANEST way to do this.

If x = 19767 and I want bit3 - bit8 (starting from the right): 100110100110111 is 19767 in binary. I want the part in parenthesis 100110(100110)111 so the answer is 38.

What is the simplest/cleanest/most-elegant way to implement the following function with Ruby?

bit_range(orig_num, first_bit, last_bit)

PS. Bonus points for answers that are computationally less intensive.

4

6 に答える 6

3
orig_num.to_s(2)[(-last_bit-1)..(-first_bit-1)].to_i(2)
于 2012-11-08T22:34:35.417 に答える
3
19767.to_s(2)[-9..-4].to_i(2)

また

19767 >> 3 & 0x3f

アップデート:

スープからナッツまで (とにかく、なぜ人々はそう言うのでしょうか?) ...

class Fixnum
  def bit_range low, high
    len = high - low + 1
    self >> low & ~(-1 >> len << len)
  end
end

p 19767.bit_range(3, 8)
于 2012-11-08T22:35:06.543 に答える
2

提案された回答の速度を示すためだけに:

require 'benchmark'

ORIG_NUMBER = 19767

def f(x,i,j)
  b = x.to_s(2)
  n = b.size
  b[(n-j-1)...(n-i)].to_i(2)
end

class Fixnum
  def bit_range low, high
    len = high - low + 1
    self >> low & ~(-1 >> len << len)
  end

  def slice(range_or_start, length = nil)
    if length
      start = range_or_start
    else
      range = range_or_start
      start = range.begin
      length = range.count
    end

    mask = 2 ** length - 1

    self >> start & mask
  end
end

def p n
  puts "0b#{n.to_s(2)}"; n
end

n = 1_000_000
puts "Using #{ n } loops in Ruby #{ RUBY_VERSION }."
Benchmark.bm(21) do |b|
  b.report('texasbruce') { n.times { ORIG_NUMBER.to_s(2)[(-8 - 1)..(-3 - 1)].to_i(2) } }
  b.report('DigitalRoss string') { n.times { ORIG_NUMBER.to_s(2)[-9..-4].to_i(2) } }
  b.report('DigitalRoss binary') { n.times { ORIG_NUMBER >> 3 & 0x3f } }
  b.report('DigitalRoss bit_range') { n.times { 19767.bit_range(3, 8) } }
  b.report('Philip') { n.times { f(ORIG_NUMBER, 3, 8) } }
  b.report('Semyon Perepelitsa') { n.times { ORIG_NUMBER.slice(3..8) } }
end

そして出力:

Using 1000000 loops in Ruby 1.9.3.
                            user     system      total        real
texasbruce              1.240000   0.010000   1.250000 (  1.243709)
DigitalRoss string      1.000000   0.000000   1.000000 (  1.006843)
DigitalRoss binary      0.260000   0.000000   0.260000 (  0.262319)
DigitalRoss bit_range   0.840000   0.000000   0.840000 (  0.858603)
Philip                  1.520000   0.000000   1.520000 (  1.543751)
Semyon Perepelitsa      1.150000   0.010000   1.160000 (  1.155422)

それは私の古いMacBookProにあります。マイレージは異なる場合があります。

于 2012-11-09T14:57:03.403 に答える
2

純粋な数値演算を使用してこれを行う方法は次のとおりです。

class Fixnum
  def slice(range_or_start, length = nil)
    if length
      start = range_or_start
    else
      range = range_or_start
      start = range.begin
      length = range.count
    end

    mask = 2 ** length - 1

    self >> start & mask
  end
end

def p n
  puts "0b#{n.to_s(2)}"; n
end

p 0b100110100110111.slice(3..8) # 0b100110
p 0b100110100110111.slice(3, 6) # 0b100110
于 2012-11-09T14:59:47.313 に答える
1

そのための関数を定義することは理にかなっています:

def f(x,i,j)
  b = x.to_s(2)
  n = b.size
  b[(n-j-1)...(n-i)].to_i(2)
end

puts f(19767, 3, 8) # => 38
于 2012-11-08T22:39:38.297 に答える