pyscg-0003: Use Arithmetic Over Bitwise Operations
Avoid mixing bitwise shifts with arithmetic operations, instead, use clear mathematical expressions instead to maintain predictable behavior, readability, and compatibility.
Ensure to know what bit-wise shift operators do in case you can not avoid them as recommended in NUM01-J. Do not perform bitwise and arithmetic operations on the same data [SEI CERT JAVA 2024] and use math instead.
There are two C and C++ design patterns for resource optimisation that are no longer promoted:
- Bit-wise operations for divisions or multiplication shifting the whole content of a variable to left or right for increased speed.
- Flag registers, or Boolean’s stored in a single bit of an int or byte for space reduction.
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.
A need to use bit-wise operations in Python can be due to translations or dealings with C, C++ or Java, system libraries, raw binary data, or cryptographic algorithms. Existing Python modules hooking into system C libraries for cryptographic functions or math all to avoid the need to implement bit-shifting on a Python level. Bit-shifting can have unexpected outcomes. Python’s ctypes module allows integration of C based system libraries into Python and direct access to Fixed-Width numbers that can have different behavior than using high-level Python. Understanding ctypes or C requires understanding the CERT C Coding Standard [SEI CERT C 2025] and setting boundaries manually in Python.
Why This Matters
- Precedence traps:
+/-bind tighter than<</>>, so1 << n + 1means1 << (n + 1). Use parentheses when shifts appear in compound expressions. - Sign-extension surprise: Right shift on negative values is arithmetic (sign-extending), not logical. People expecting zero-fill get caught out.
- Unbounded growth: Python
intdoesn’t overflow, so1 << nin a loop can explode memory/performance. - Boundary code: When working with fixed-width integers (
ctypes, NumPy dtypes, packed binary formats), C/ABI-like fixed-width semantics apply—handle width, signedness, and wraparound explicitly.
When Bitwise Is Appropriate
Bitwise operations are fine when data is actually a bit collection: flags/masks, serialization, binary protocols, cryptographic primitives. Keep such code localized, document the assumed bit width, and prefer helper functions for packing/unpacking.
Non-compliant Code Example (Left Shift)
Multiplication by 4 can be achieved by a 2x left shift. The noncompliant01.py code demonstrates an attempt to calculate 8 * 4 + 10 in one line.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Non-compliant Code Example """
print(8 << 2 + 10)
The noncompliant01.py code results in printing 32768 instead of 42. Adding brackets print((8 << 2) + 10) would fix this specific issue whilst still remaining prone to other issues.
Compliant Solution (Left Shift)
The statement in compliant01.py clarifies the programmer’s intention.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Compliant Code Example """
print(8 * 4 + 10)
It is recommended by pyscg-0002: Guard Fixed-Width Numbers Against Overflow to also check for under or overflow.
Non-compliant Code Example (Bitfield Flags)
Flags are commonly packed into a single integer and combined with bitwise OR (for example re.IGNORECASE | re.MULTILINE or os.O_RDWR | os.O_CREAT). The noncompliant02.py code treats such a bitfield as a number and uses arithmetic += to set a flag.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Non-compliant Code Example """
FLAG_READ = 0b0001
FLAG_WRITE = 0b0010
FLAG_EXECUTE = 0b0100
perms = FLAG_READ | FLAG_EXECUTE
perms += FLAG_WRITE # arithmetic on a bitfield
perms += FLAG_WRITE # setting WRITE again carries into the next bit
print(f"perms = {perms:04b}")
print(f"has WRITE? {bool(perms & FLAG_WRITE)}")
print(f"has EXECUTE? {bool(perms & FLAG_EXECUTE)}")
Adding FLAG_WRITE the first time happens to work, but adding it a second time carries into the neighbouring bit, changing 0111 to 1001. Both WRITE and EXECUTE now read as unset and an undefined bit is set instead, silently corrupting a later permission check.
Compliant Solution (Bitfield Flags)
The compliant02.py code uses bitwise OR |= to set the flag. Unlike addition, OR is idempotent, so setting an already-set flag is harmless.
# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
""" Compliant Code Example """
FLAG_READ = 0b0001
FLAG_WRITE = 0b0010
FLAG_EXECUTE = 0b0100
perms = FLAG_READ | FLAG_EXECUTE
perms |= FLAG_WRITE # bitwise OR sets the bit
perms |= FLAG_WRITE # idempotent: setting it again is harmless
print(f"perms = {perms:04b}")
print(f"has WRITE? {bool(perms & FLAG_WRITE)}")
print(f"has EXECUTE? {bool(perms & FLAG_EXECUTE)}")
For flag sets, prefer enum.IntFlag, which models bit flags as first-class members and prevents accidental arithmetic on them.
Automated Detection
Not available
Related Guidelines
Bibliography
| [Rusher 2017] | Python internals: Arbitrary-precision integer implementation [online]. Available from: https://rushter.com/blog/python-integer-implementation/ [accessed 8 May 2024] |
| [SEI CERT JAVA 2024] | NUM01-J. Do not perform bitwise and arithmetic operations on the same data [online]. Available from: https://wiki.sei.cmu.edu/confluence/display/java/NUM01-J.+Do+not+perform+bitwise+and+arithmetic+operations+on+the+same+data [Accessed 6 May 2025] |
| [SEI CERT C 2025] | CERT C Coding Standard [online]. Available from: https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard [Accessed 6 May 2025] |