I frequently miss the edge cases like while num or while num > 1.
You did that here. What if num
is 0 or 1.75? You'll get a TypeError
when your function performs num >>= 1
in the case num
is 1.75, and maybe that's what you want. The function will return 0 in the case num
is zero.
As others have noted, your function only works for base==2
. If you truly meant a base 2 function only, then you shouldn't promise that you can handle any base. An alternative to using log for this base 2 logarithm is to use the fact that python provides a function available on all platforms (even those that don't use IEEE floating point) that does exactly what you want:
import math
def floor_log2 (num) :
if (num <= 0) :
raise ValueError("Positive number only.")
mantissa, res = math.frexp(num)
if (mantissa == 0.5) :
res -= 1
return res-1
This works because the job of math.frexp(x)
is to
return the mantissa and exponent of x
as the pair (m, e)
. m
is a float and e
is an integer such that x == m * 2**e
exactly. If x
is zero, returns (0.0, 0)
, otherwise 0.5 <= abs(m) < 1
. This is used to “pick apart” the internal representation of a float in a portable way.
Your function and mine have a similar edge case behavior: The name does not quite agree with the return value in the case the input is an integer power of two (or integer power of the base in your case). The docstring should clear document the nature of the return value in these edge cases.
Finally, your function does not return the floor of the log base 2. It returns 2floor_log2(n). Renaming it largest_power_of_two
,
import math
def largest_power_of_two (num) :
""""Returns the integral power of two that is strictly less than num"""
if (num <= 0) :
raise ValueError("Positive number only.")
mantissa, res = math.frexp(num)
if (mantissa == 0.5) :
res -= 1
if res > 0 :
return 1 << (res-1)
else :
return 1.0 / (1 << (1-res))