Ruby - 从变量获取位范围

时间:2012-11-08 22:28:43

标签: ruby bit-manipulation

我有一个变量,想要从该变量中获取一系列位。我想要最干净的方式来做到这一点。

如果x = 19767我想要bit3 - bit8(从右边开始): 二进制文件中10011010011011119767。 我想要括号100110(100110)111中的部分,所以答案是38。

使用Ruby实现以下功能的最简单/最干净/最优雅的方法是什么?

bit_range(orig_num, first_bit, last_bit)

PS。对计算密集程度较低的答案的奖励积分。

6 个答案:

答案 0 :(得分:3)

orig_num.to_s(2)[(-last_bit-1)..(-first_bit-1)].to_i(2)

答案 1 :(得分: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)

答案 2 :(得分: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)

那是我原来的MacBook Pro。您的里程可能会有所不同。

答案 3 :(得分: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

答案 4 :(得分: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

答案 5 :(得分:0)

扩展DigitalRoss的想法 - 您可以传递范围:

,而不是采用两个参数
class Fixnum
  def bit_range range
    len = range.last - range.first + 1
    self >> range.first & ~(-1 >> len << len)
  end
end

19767.bit_range 3..8