Hexadecimal multiplication is the mathematical process of multiplying base-16 numbers, where digits range from 0-9 and A-F, following the same positional carry rules as decimal math but rolling over at 16 instead of 10. While the physics of your circuit does not care what base you count in, hexadecimal multiplication directly changes how you calculate memory offsets, configure I2C hardware registers, and scale PWM values in embedded C/C++ code. People commonly confuse multiplying by a hex value with bitwise shifting, or they mistakenly treat hex letters (A-F) as algebraic variables rather than fixed integers. Let us clear up the math so your next firmware flash actually works.
The Core Mechanics of Hexadecimal Multiplication
When you multiply in base-16, the fundamental algorithm is identical to the long multiplication you learned in grade school. The only difference is your threshold for carrying over to the next column. In decimal, you carry when a product hits 10. In hex, you carry when a product hits 16.
Let us multiply 0x1A (which is 26 in decimal) by 0x03 (3 in decimal). The expected decimal result is 78, which translates to 0x4E in hex.
- Multiply the least significant digit: 3 × A. Since A represents 10, the product is 30.
- Calculate the carry and remainder: Divide 30 by 16. You get 1 with a remainder of 14. The remainder 14 translates to the hex digit E. Write down E, carry the 1.
- Multiply the next digit and add the carry: 3 × 1 = 3. Add the carried 1 to get 4.
- Final Result: Combine the digits to get 0x4E.
The most frequent mistake hobbyists make on the bench is mixing bases in their head. They see 'A' and unconsciously switch to decimal math for that specific column, breaking the carry chain. If you are doing hex math, every single column must roll over at 16.
Where You Meet This in Practice
You will rarely need to multiply large hex numbers by hand, but base-16 multiplication is the hidden engine behind several everyday embedded systems tasks:
- I2C Sub-Addressing: When configuring a device like the MCP23017 I/O expander, you often calculate register offsets by multiplying the bank index by a hex stride value to find the correct configuration byte.
- RGB LED Color Scaling: If you are driving WS2812B LEDs and want to scale a 24-bit hex color (like 0x112233) to 50% brightness, you must multiply each individual byte (0x11, 0x22, 0x33) by a scaling factor, ensuring you handle the hex carry correctly so the colors do not bleed into adjacent channels.
- SPI Flash Memory Erase Blocks: Chips like the W25Q32 require erase commands aligned to 4KB (0x1000) sectors. Calculating the starting address of sector 0x0C requires multiplying 0x0C by 0x1000 to yield 0x0C000.
Real-World Scenario Walkthrough: The Misconfigured OLED Buffer
To understand what happens when hex multiplication goes wrong, let us look at a classic firmware bug involving an SSD1306 128x64 OLED display. For a deep dive into the hardware itself, refer to the Adafruit SSD1306 Guide.
The Setup: You are writing a custom font renderer in C++ for an Arduino. The display buffer is a 1024-byte linear array. The screen is organized into 8 horizontal 'pages' (each 8 pixels tall) and 128 columns. You need to calculate the exact linear buffer index to place an 8x8 character at X-coordinate 0x14 (20) and Y-page 0x03 (3).
The Numbers: The formula for the linear offset is: Offset = (Page × 128) + Column.
In hex, 128 is 0x80.
The math required is: (0x03 × 0x80) + 0x14.
The Outcome: The developer looks at 0x03 × 0x80, mentally reads it as '3 times 80', and does decimal math in their head: 240. They then add the column value, treating 0x14 as decimal 14, getting 254. They convert 254 to hex (0xFE) and write the character data to index 0xFE in the buffer.
What Went Wrong: The correct hex multiplication for 0x03 × 0x80 is 0x180 (384 in decimal). Adding the hex column 0x14 (20 in decimal) yields a true offset of 0x194 (404 in decimal). By writing to index 0xFE instead of 0x194, the developer wrote the character data into Page 1 instead of Page 3, overwriting the top header of their UI and leaving garbage pixels on the third row.
Hex Multiplication vs. Bitwise Shifting
The most common conceptual confusion in embedded programming is treating hex multiplication and bitwise shifting as entirely different operations. When you multiply a hex number by a power of 16 (like 0x10, 0x100, 0x1000), it is mathematically identical to shifting the bits to the left.
| Operation | Hex Math Equivalent | C/C++ Syntax | Execution Speed (AVR/ARM) |
|---|---|---|---|
| Multiply by 16 | Value × 0x10 | val << 4 |
1 Cycle (Shift is faster) |
| Multiply by 256 | Value × 0x100 | val << 8 |
1 Cycle (Shift is faster) |
| Multiply by 3 | Value × 0x03 | val * 3 |
Multiple Cycles (No direct shift) |
| Multiply by 10 | Value × 0x0A | val * 10 |
Multiple Cycles (No direct shift) |
If you are communicating over I2C and need to construct a 16-bit register address from two 8-bit bytes, you do not need to multiply the high byte by 0x100. You simply cast it to a 16-bit integer and shift it left by 8 bits. The Arduino Wire Library handles much of this under the hood, but when you write custom drivers for sensors like the BME280, understanding this equivalence saves CPU cycles and prevents overflow bugs.
Frequently Asked Questions
Do I need to memorize the hex multiplication table?
No. Unless you are writing a bootloader in raw assembly without access to a compiler, you should rely on your IDE or a programmer calculator. However, you must memorize the decimal equivalents of A through F (10 through 15) so you can instantly recognize when a mental carry is required.
How do I handle hex multiplication overflow in 8-bit registers?
If you multiply two 8-bit hex values (e.g., 0xFF × 0x02), the result is 0x1FE, which requires 9 bits. If you assign this directly to an 8-bit uint8_t variable in C, the compiler will silently truncate the high byte, leaving you with 0xFE. Always assign the result of hex multiplication to a 16-bit (uint16_t) or 32-bit (uint32_t) variable first, then mask or shift the bits you actually need.
Why do we use hex instead of binary for these calculations?
Binary is physically accurate to the logic gates, but it is visually exhausting to read. A 32-bit memory address in binary is 32 characters long. In hex, it is exactly 8 characters. Hexadecimal acts as a perfect human-readable compression layer for binary, because every single hex digit maps exactly to four binary bits (a nibble), making mental conversion between the two trivial once you learn the pattern.






