Search code examples
pythonbinarytwos-complement

"Bitwise Not" in Python disconsidering 2's complement


I need to do a "~" operation in Python but not taking account of 2's complement. I managed to do that by using XOR, do you know another way to do this? (more efficient)

a = 0b101
b = 0b10101

print bin(a ^ (2 ** a.bit_length() - 1)) #0b10
print bin(b ^ (2 ** b.bit_length() - 1)) #0b1010

Solution

  • That's what ~ does already. The tricky part is that Python has unlimited length integers, so when you invert a number it is sign extended with--at least conceptually speaking--an infinite number of 1's. Meaning you get negative numbers.

    >>> bin(~0b101)
    '-0b110'
    >>> bin(~0b10101)
    '-0b10110'
    

    To convert these to unsigned numbers, you need to decide how many bits you care about. Maybe you are working with 8-bit bytes. Then you could AND them with a byte's worth of 1 bits:

    >>> bin(~0b101 & 0xFF)
    '0b11111010'
    >>> bin(~0b10101 & 0xFF)
    '0b11101010'
    

    Or if you want to match the exact bit length of the input numbers, your solution is reasonable. For efficiency you could switch the exponent for a left shift. And it might be clearer to use ~ and & instead of ^.

    >>> bin(~a & ((1 << a.bit_length()) - 1))
    '0b10'
    >>> bin(~b & ((1 << b.bit_length()) - 1))
    '0b1010'
    

    (I suspect a hardcoded mask like & 0xFFFF will be the right solution in practice. I can't think of a good real world use case for the bit_length()-based answer.)