Shawn - 1 year ago 61
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?

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
``````
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download