C
and C++
used to have two design patterns in order to optimize resource utilization:
C
and C++
no longer promote these design patterns. The use of bit-wise operations for arithmetic or flag registers can reduce readability, predictability of the code and can also cause compatibility issues. Some bit-wise operations can reduce performance. Python tries to safeguard changes between positive and negative numbers by storing the sign separately. It tries to prevent overflows by using either 32-bit unsigned integer
arrays with 30-bit
digits or 16-bit
unsigned integer arrays with 15-bit
digit [Rusher 2017]. In other words, Python changes and adapts on the fly.
The example01.py
code demonstrates bit-wise operators available in Python.
foo = 50
bar = 42
print(f"foo = {foo} = {foo:08b}") # :08b is just for pretty print
print(f"foo = {bar} = {bar:08b}\n")
# bit wise operations in Python:
print(f"foo << 2 = {(foo << 2):08b}") # binary shift left
print(f"foo >> 2 = {(foo >> 2):08b}") # binary shift right
print(f"~foo = {(~foo):08b}") # flipping bits
print(f"foo & bar = {(foo & bar):08b}") # binary AND
print(f"foo | bar = {(foo | bar):08b}") # binary OR
print(f"foo ^ bar = {(foo ^ bar):08b}") # binary XOR
Output from above example01.py:
foo = 50 = 00110010
foo = 42 = 00101010
foo << 2 = 11001000
foo >> 2 = 00001100
~foo = -0110011
foo & bar = 00100010
foo | bar = 00111010
foo ^ bar = 00011000
The example02.py
code demonstrates how Python 2 changes an int to long to prevent an overflow condition while Python 3 is always storing an int
as long
[Python 3.10.5 2022].
for shift in [16, 32, 64]:
bar = 5225 << shift
print("foo << " + str(shift) + ": type " + str(type(bar)) + " " + str(bin(bar)))
Left shift in example02.py
changes type to long class in Python 2:
foo << 16: type <type 'int'> 0b10100011010010000000000000000
foo << 32: type <type 'int'> 0b101000110100100000000000000000000000000000000
foo << 64: type <type 'long'> 0b10100011010010000000000000000000000000000000000000000000000000000000000000000
Left shift in example02.py
stays type int class but stores as long Python 3:
foo << 16: type <class 'int'> 0b10100011010010000000000000000
foo << 32: type <class 'int'> 0b101000110100100000000000000000000000000000000
foo << 64: type <class 'int'> 0b10100011010010000000000000000000000000000000000000000000000000000000000000000
Multiplication by 4
can be archived by a 2x
left. The noncompliant01.py
code demonstrates an attempt to calculate 8 * 4 + 10
in one line.
""" Non-compliant Code Example """
print(8 << 2 + 10)
The noncompliaint01.py
code results in printing 32768
instead of 42
. Adding brackets print((8 << 2) + 10)
would fix this specific issue whilst still remaining prune to other issues.
The statement in compliant01.py
clarifies the programmer’s intention.
""" Compliant Code Example """
print(8 * 4 + 10)
It is recommended by CWE-191, Integer Underflow (Wrap or Wraparound) to also check for under or overflow.
In this non-compliant code example is using an arithmetic right shift »= operator in an attempt to optimize performance for dividing x by 4 without floating point.
""" Non-compliant Code Example """
foo: int
foo = -50
foo >>= 2
print(foo)
The expectation of the >>= 2
right shift operator is that it fills the leftmost bits of 0011 0010
with two zeros resulting in 0000 1100
or decimal twelve. Instead a potentially expected -12
in foo
we have internal processing truncating the values from -12.5
to -13
.
The right shift is replaced by division in compliant02.py
.
""" Compliant Code Example """
foo: int
foo = -50
foo /= 4
print(foo)
unknown
[Rusher 2017] | Python internals: Arbitrary-precision integer implementation [online]. Available from: https://rushter.com/blog/python-integer-implementation/ [accessed 8 May 2024] |
[Python 3.9 2022] | Build-in Types [online]. Available from: https://docs.python.org/3.9/library/stdtypes.html [accessed 8 May 2024] |