Is unsetting a single bit in flags safe with Python variable-length integers?
Question:
In my program (written in Python 3.4) I have a variable which contains various flags, so for example:
FLAG_ONE = 0b1
FLAG_TWO = 0b10
FLAG_THREE = 0b100
status = FLAG_ONE | FLAG_TWO | FLAG_THREE
Setting another flag can easily be done with
status |= FLAG_FOUR
But what if I explicitly want to clear a flag? I’d do
status &= ~FLAG_THREE
Is this approach safe? As the size of an integer in Python is not defined, what if status
and FLAG_THREE
differ in size?
(status
needs to be a bit field because I need this value for a hardware protocol.)
Answers:
Clearing a flag works with
status &= ~FLAG_THREE
because Python treats those negated values as negative:
>>> ~1L
-2L
>>> ~1
-2
>>> ~2
-3
Thus the &
operator can act appropriately and yield the wanted result irrespective of the length of the operands, so 0b11111111111111111111111111111111111111111111111111111111111 & ~1
works fine although the left hand operand is biffer than the right hand one.
In the other direction (RH longer than LH), it works nevertheless, because having an excess number of 1
bits doesn’t matter.
You should be safe using that approach, yes.
~
in Python is simply implemented as -(x+1)
(cf. the CPython source) and negative numbers are treated as if they have any required number of 1s padding the start. From the Python Wiki:
Of course, Python doesn’t use 8-bit numbers. It USED to use however many bits were native to your machine, but since that was non-portable, it has recently switched to using an INFINITE number of bits. Thus the number -5 is treated by bitwise operators as if it were written "…1111111111111111111011".
In other words, with bitwise-and &
you’re guaranteed that those 1s will pad the length of ~FLAG
(a negative integer) to the length of status
. For example:
100000010000 # status
& ~10000 # ~FLAG
is treated as
100000010000
& 111111101111
= 100000000000 # new status
This behaviour is described in a comment in the source here.
In my program (written in Python 3.4) I have a variable which contains various flags, so for example:
FLAG_ONE = 0b1
FLAG_TWO = 0b10
FLAG_THREE = 0b100
status = FLAG_ONE | FLAG_TWO | FLAG_THREE
Setting another flag can easily be done with
status |= FLAG_FOUR
But what if I explicitly want to clear a flag? I’d do
status &= ~FLAG_THREE
Is this approach safe? As the size of an integer in Python is not defined, what if status
and FLAG_THREE
differ in size?
(status
needs to be a bit field because I need this value for a hardware protocol.)
Clearing a flag works with
status &= ~FLAG_THREE
because Python treats those negated values as negative:
>>> ~1L
-2L
>>> ~1
-2
>>> ~2
-3
Thus the &
operator can act appropriately and yield the wanted result irrespective of the length of the operands, so 0b11111111111111111111111111111111111111111111111111111111111 & ~1
works fine although the left hand operand is biffer than the right hand one.
In the other direction (RH longer than LH), it works nevertheless, because having an excess number of 1
bits doesn’t matter.
You should be safe using that approach, yes.
~
in Python is simply implemented as -(x+1)
(cf. the CPython source) and negative numbers are treated as if they have any required number of 1s padding the start. From the Python Wiki:
Of course, Python doesn’t use 8-bit numbers. It USED to use however many bits were native to your machine, but since that was non-portable, it has recently switched to using an INFINITE number of bits. Thus the number -5 is treated by bitwise operators as if it were written "…1111111111111111111011".
In other words, with bitwise-and &
you’re guaranteed that those 1s will pad the length of ~FLAG
(a negative integer) to the length of status
. For example:
100000010000 # status
& ~10000 # ~FLAG
is treated as
100000010000
& 111111101111
= 100000000000 # new status
This behaviour is described in a comment in the source here.