Ruby - get a range of bits from a variable

I have a variable and you want to take a series of bits from this variable. I want to use CLEANEST.

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

What is the easiest / 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 less intense in terms of the number of calculations.

+4
source share
6 answers
 19767.to_s(2)[-9..-4].to_i(2) 

or

 19767 >> 3 & 0x3f 

Update:

Nuts soup (why do people say that?) ...

 class Fixnum def bit_range low, high len = high - low + 1 self >> low & ~(-1 >> len << len) end end p 19767.bit_range(3, 8) 
+3
source
 orig_num.to_s(2)[(-last_bit-1)..(-first_bit-1)].to_i(2) 
+3
source

Just to show the speed of the suggested answers:

 require 'benchmark' ORIG_NUMBER = 19767 def f(x,i,j) b = x.to_s(2) n = b.size b[(nj-1)...(ni)].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 pn 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 

And the conclusion:

 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) 

What's on my old MacBook Pro. Your mileage may vary.

+2
source

Here's how to do this using clean number operations:

 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 pn puts "0b#{n.to_s(2)}"; n end p 0b100110100110111.slice(3..8) # 0b100110 p 0b100110100110111.slice(3, 6) # 0b100110 
+2
source

It makes sense to define a function for this:

 def f(x,i,j) b = x.to_s(2) n = b.size b[(nj-1)...(ni)].to_i(2) end puts f(19767, 3, 8) # => 38 
+1
source

Extending the idea of ​​DigitalRoss - instead of two arguments, you can pass a range:

 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 
0
source

Source: https://habr.com/ru/post/1444955/


All Articles