Shawn Shawn - 1 month ago 5
Ruby Question

Create a method to find if a number is a power of 2?

I have this code to return

true
if
num
is a power of 2.

def is_power_of_two?(num)
result = num.inject(0) {|n1, n2| n2 ** n1}
if result == num
true
else
false
end
end

p is_power_of_two?(16)


I keep getting an error though. How could I fix and simplify this code?

Answer

Code

R = /
    \A      # match beginning of string ("anchor")
    10*     # match 1 followed by zero or more zeroes
    \z      # match end of string ("anchor")
    /x      # free-spacing regex definition mode

def po2?(n)
  !!(n.to_s(2) =~ R)
end

Examples

po2?  0     #=> false
po2?  1     #=> true
po2? 32     #=> true
po2? 33     #=> false
po2? -7     #=> false

Explanation

Fixnum#to_s provides the string representation of an integer (the receiver) for a given base. The method's argument, which defaults to 10, is the base. For example:

16.to_s     #=> "16" 
16.to_s(8)  #=> "20" 
16.to_s(16) #=> "10"
15.to_s(16) #=>  "f"

It's base 2 we're interested in. For powers of 2:

 1.to_s(2)  #=>     "1" 
 2.to_s(2)  #=>    "10" 
 4.to_s(2)  #=>   "100" 
 8.to_s(2)  #=>  "1000"
16.to_s(2)  #=> "10000"

For a few natural numbers that are are not powers of 2:

 3.to_s(2)  #=>   "11" 
 5.to_s(2)  #=>  "101" 
11.to_s(2)  #=> "1011" 

We therefore wish to match strings that begin with 1 and are followed by zero or more zeroes. The regex above (R) does that.

!! is a trick to convert "falsy" (false or nil) or "truthy" (everything that's not falsy) values to false and true respectively. Suppose, for example, n.to_s(2) =~ R returned 0, meaning there is a match starting at offset zero. Then:

!!0 #=> !(!0) => !(false) => true

Similarly, if there is no match:

!!nil #=> !(!nil) => !(true) => false

!! is not necessary if you are only concerned with whether the return value is truthy or falsy.

Another Way

This uses Fixnum#bit_length and Fixnum#[]:

def po2?(n)
  m = n.bit_length-1
  n[m] == 1 and m.times.all? { |i| n[i].zero? }
end

po2?  0     #=> false
po2?  1     #=> true
po2? 32     #=> true
po2? 33     #=> false
po2? -7     #=> false