Search code examples
pythonlogicoperator-keyword

Problem with the "and" operator in python


I was working on what seemed an easy exercise but I ran into a problem with a the "and" orperator. I think I understand quite well how it works:

print("0 and 0 : ", (0 and 0))
print("0 and 1 : ", (0 and 1))
print("1 and 0 : ", (1 and 0))
print("1 and 1 : ", (1 and 1))
print((0 and 1) == (1 and 0))

That gives me the expected results :

0 and 0 :  0
0 and 1 :  0
1 and 0 :  0
1 and 1 :  1
True

BUT when I run this code:

A = '00111000111000111000111000'
B = '00001110001110001110001110'

for i in range(len(A)):
    s1s2 = (A[i] and B[i])
    s2s1 = (B[i] and A[i])
    print(f"s1={A[i]}, s2={B[i]}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))

I end up with non-sense results in my console using Python3.9 on VSCode(and I tried online too):

s1=1, s2=0, s1 and s2 = 0 ; s2 and s1 = 1 : False
s1=0, s2=1, s1 and s2 = 1 ; s2 and s1 = 0 : False

The same problem goes with "or".

A = '00111000111000111000111000'
B = '00001110001110001110001110'
for i in range(len(A)):
    s1s2 = (A[i] or B[i])
    s2s1 = (B[i] or A[i])
    print(f"s1={A[i]}, s2={B[i]} || s1 or s2 = {s1s2} ; s2 or s1 = {s2s1}  || s1s2 == s2s1 : ", (s1s2) == (s2s1))

That returns non-sense things :

s1=1, s2=0 || s1 or s2 = 1 ; s2 or s1 = 0  || s1s2 == s2s1 :  False
s1=0, s2=1 || s1 or s2 = 0 ; s2 or s1 = 1  || s1s2 == s2s1 :  False

I've tried to transform the string A and B into lists before running the loop but it gave me the same results. Do you have any idea about what's going on here? What I am missing or doing wrong ?


Solution

  • Let's take a slightly different case:

    0 and None -> 0
    None and 0 -> None
    '1' and '0' -> '0'
    '0' and '1' -> '1'
    

    Why? Well in the first two cases, both 0 (an integer) and None are falsy, so the first one is returned. In the second two cases, you have non-empty strings, both of which are truthy, so the second one is returned.

    There are a couple of ways to fix the second result to be there same as integer comparison. The simplest way is to convert each bit to integer on demand:

    A = '00111000111000111000111000'
    B = '00001110001110001110001110'
    for a, b in zip(A, B):
        a = int(a)
        b = int(b)
        s1s2 = (a and b)
        s2s1 = (b and a)
        print(f"s1={a}, s2={b}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))
    

    Another way is to work with integers directly and use bit-twiddling. This requires you to set the number of bits explicitly, since something like int(A).bit_length() will discard any leading zeroes:

    N = 26
    A = 0b00111000111000111000111000
    B = 0b00001110001110001110001110
    for i in range(N - 1, -1, -1):
        a = (A & (1 << i)) >> i
        b = (B & (1 << i)) >> i
        s1s2 = (a and b)
        s2s1 = (b and a)
        print(f"s1={a}, s2={b}, s1 and s2 = {s1s2} ; s2 and s1 = {s2s1} :", (s1s2) == (s2s1))
    

    You can omit >> i in both cases since it does not affect the truth value of the bit selected by (X & (1 << i)). Also, 1 << i == 2**i.