Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange if statement

I found this strange if-statement in somebody else’s code:

if variable & 1 == 0:

I don't understand it. It should have two ==, right?

Can somebody explain this?

like image 390
n0tis Avatar asked Apr 16 '15 00:04

n0tis


4 Answers

The conditional is a bitwise operator comparison:

>>> 1 & 1
1
>>> 0 & 1
0
>>> a = 1
>>> a & 1 == 0
False
>>> b = 0
>>> b & 1 == 0
True

As many of the comments say, for integers this conditional is True for evens and False for odds. The prevalent way to write this is if variable % 2 == 0: or if not variable % 2:

Using timeit we can see that there isn't much difference in performance.

n & 1("== 0" and "not")

>>> timeit.Timer("bitwiseIsEven(1)", "def bitwiseIsEven(n): return n & 1 == 0").repeat(4, 10**6)
[0.2037370204925537, 0.20333600044250488, 0.2028651237487793, 0.20192503929138184]

>>> timeit.Timer("bitwiseIsEven(1)", "def bitwiseIsEven(n): return not n & 1").repeat(4, 10**6)
[0.18392395973205566, 0.18273091316223145, 0.1830739974975586, 0.18445897102355957]

n % 2("== 0" and "not")

>>> timeit.Timer("modIsEven(1)", "def modIsEven(n): return n % 2 == 0").repeat(4, 10**6)
[0.22193098068237305, 0.22170782089233398, 0.21924591064453125, 0.21947598457336426]

>>> timeit.Timer("modIsEven(1)", "def modIsEven(n): return not n % 2").repeat(4, 10**6)
[0.20426011085510254, 0.2046220302581787, 0.2040550708770752, 0.2044820785522461]

Overloaded Operators:

Both the % and & operators are overloaded.

The bitwise and operator is overloaded for set. s.intersection(t) is equivalent to s & t and returns a "new set with elements common to s and t".

>>> {1} & {1}
set([1])

This doesn't effect our conditional:

>>> def bitwiseIsEven(n):
...   return n & 1 == 0

>>> bitwiseIsEven('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in bitwiseIsEven
TypeError: unsupported operand type(s) for &: 'str' and 'int'
>>> bitwiseIsEven({1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in bitwiseIsEven
TypeError: unsupported operand type(s) for &: 'set' and 'int'

The modulo operator will also throw TypeError: unsupported operand type(s) for most non-ints.

>>> def modIsEven(n):
...   return n % 2 == 0

>>> modIsEven({1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in modIsEven
TypeError: unsupported operand type(s) for %: 'set' and 'int'

It is overloaded as a string interpolation operator for the old %-formatting. It throws TypeError: not all arguments converted during string formatting if a string is used for the comparison.

>>> modIsEven('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in modIsEven
TypeError: not all arguments converted during string formatting

This won't throw if the string includes a valid conversion specifier.

>>> modIsEven('%d')
False 
like image 109
dting Avatar answered Nov 09 '22 08:11

dting


This code just checks if the lowest bit of variable is a 0. Based on operator precedence this is:

if (variable & 1) == 0:

First AND the lowest bit with one (extract just the lowest bit), then check if it is 0.

like image 31
Brad Budlong Avatar answered Nov 09 '22 09:11

Brad Budlong


The & is a bitwise operator. It returns an integer with 1 bit for every bit of its two operands that are both 1, and 0 in all other places. For example:

a = 10 # 0b1010
b = 6  # 0b0110
a & b  # 0b0010

Now, if you have variable & 1, you're comparing variable against 0b1 which will only return 1 if that last digit in the binary representation is a 1, otherwise a 0.

like image 14
MasterOdin Avatar answered Nov 09 '22 07:11

MasterOdin


Your only concern is probably the operator &. It is a bitwise and which takes the binary format of the two operands and perform "logic and" on each pair of bits.

For your example, consider the following:

variable = 2  #0b0010
if variable & 1 == 0:
    print "condition satisfied" # satisfied, 0b0010 & 0b0001 = 0

variable = 5  #0b0101
if variable & 1 == 0:
    print "condition satisfied" # not satisfied, 0b0101 & 0b0001 = 1

Note:

variable = 6  #0b0110
if variable & 2 == 0:
    print "condition satisfied" # not satisfied, 0b0110 & 0b0010 = 2 (0b0010)
like image 6
Thomas Hsieh Avatar answered Nov 09 '22 08:11

Thomas Hsieh