5

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

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

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

bit_range(orig_num, first_bit, last_bit)

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

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)

那是在我的旧 MacBook Pro 上。您的里程可能会有所不同。

于 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 回答
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
于 2014-12-27T09:39:55.410 回答