5.07 Modulo (%) vs Bitwise AND (& 1) & Branchless Counting in Assembly
Executive Summary: Analyzing parity checks and filtering in arrays. We contrast expensive hardware division (num % 2) with single-cycle bitwise operations (num & 1) and demonstrate branchless arithmetic algorithms that eliminate pipeline flushes.
💻 1. Annotated Source Code
#include <iostream> using namespace std; int countEvens(int arr[], int size); int main() { int myArray[] = { 12, 7, 4, 19, 22, 3, 8, 10 }; int numEvens = countEvens(myArray, 8); cout << "Number of even elements: " << numEvens << endl; return 0; } int countEvens(int arr[], int size) { int count = 0; for (int i = 0; i < size; i++) { if (arr[i] % 2 == 0) { count++; } } return count; }
📐 2. Architecture & UML Class Model
<<compilation-unit>>
EvenCounterEngine
Counting ALU
Attributes / Data Members
(none / stateless)
Operations / Methods
+countEvens(arr: const int*, size: size_t) : size_t
+countEvensBranchless(arr: const int*, size: size_t) : size_t
📚 3. Core C++ Concepts Deep-Dive
1. Parity Checking: Modulo vs Bitwise
In standard arithmetic, even numbers satisfy num % 2 == 0. In binary representations, the Least Significant Bit (LSB) determines parity: (num & 1) == 0.
⚡ 4. Embedded Systems & Hardware Reality
1. Hardware Division vs Bitwise Masking
On microcontrollers lacking hardware division (e.g. ARM Cortex-M0), modulo division (%) calls software division routines (__aeabi_idivmod) taking 20-50 cycles. Bitwise AND (& 1) executes in 1 clock cycle.
2. Branchless Counting
By computing count += (val & 1) ^ 1, code runs without if statements, eliminating CPU pipeline branch stalls.
💡 5. Production-Ready Embedded Refactoring
Branchless even-number counter:
💡 Production-Ready Refactor
#include <cstdint> #include <cstddef> size_t countEvensBranchless(const uint32_t* data, size_t len) noexcept { size_t count = 0; for (size_t i = 0; i < len; ++i) { // Branchless: (data[i] & 1) is 0 for even, 1 for odd count += (data[i] & 1) ^ 1; } return count; }
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
Q1. Why is '(num & 1) == 0' preferred over 'num % 2 == 0' on low-power microcontrollers?
Detailed Explanation:
Bitwise masking checks the least significant bit in 1 clock cycle, avoiding costly division instructions.
Q2. What is the advantage of 'branchless programming' in high-speed firmware?
Detailed Explanation:
Branchless code eliminates
if branches, ensuring the CPU pipeline flows smoothly without stalls caused by branch mispredictions.
Q3. What does '(val & 1) ^ 1' evaluate to when val is an even integer (e.g. 4)?
Detailed Explanation:
For even numbers:
val & 1 = 0. Then 0 ^ 1 = 1.
Q4. Which ARM Cortex-M core lacks a hardware integer divide instruction (SDIV/UDIV)?
Detailed Explanation:
ARM Cortex-M0 and M0+ cores omit hardware division hardware to minimize silicon area and power consumption.