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
The way to compute the Two's complement is by taking the One's complement and adding 1 . 1111 1100 would then be -12 in Two's complement. The advantage over One's complement is that there isn't a duplicate representation of 0 with 0000 0000 and 1111 1111 , hence allowing one more value to be represented -128 .
Bitwise NOT (~) That is, the presence of the most significant bit is used to express negative integers. Bitwise NOTing any number x yields -(x + 1) . For example, ~-5 yields 4 . Note that due to using 32-bit representation for numbers both ~-1 and ~4294967295 (232 - 1) results in 0 .
The first bitwise-and operation is used to sign-extend negative numbers (most significant bit is set), while the second is used to grab the remaining 11 bits. This works since integers in Python are treated as arbitrary precision two's complement values.
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.)
Another way, though some (including myself) may dispute it's any better, is:
tbl = str.maketrans("01","10")
int(bin(42)[2:].translate(tbl),2)
The first bit just sets up a translation table to invert 1
and 0
bits in a string.
The second bit gets the binary representation (42
-> 0b101010
), strips off the 0b
at the front, and inverts the bits through translation. Then you just use int(,2)
to turn that binary string back into a number.
If you can limit it to a specific width rather than using the width of the number itself, then it's a simple matter of (using the example of 32 bits):
val = val ^ 0xffff
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With