When you are writing firmware for an ESP32 or configuring direct port manipulation on an Arduino Uno, abstract math won't cut it. You need exact bit patterns to set GPIO registers, configure PWM timers, or parse I2C sensor payloads. This binary and decimal chart bridges the gap between human-readable base-10 logic and the base-2 reality of microcontroller hardware.
Below, you will find the complete reference data for 4-bit nibbles and critical 8-bit boundaries, sourced from the ISO/IEC 9899 C Standard for integer representations and the standard C++ <cstdint> limits. We will cover exactly which column to use for your specific hardware register, how bit-width truncation modifies your values, and a concrete decision path to select the right data type and mask.
How to Read This Binary and Decimal Chart
Before jumping to the numbers, you need to know which column applies to your specific installation or code block. Microcontrollers do not store 'numbers'; they store voltage states mapped to binary. Here is how to read the columns in the tables below:
- Binary (Base-2): The raw hardware state. Use this column when configuring physical shift registers (like the 74HC595) or when you need to visualize exactly which pins in a port register (like Arduino's
PORTB) are HIGH (1) or LOW (0). - Hexadecimal (Base-16): The shorthand for binary. Use this column when writing C++ bitmask literals (e.g.,
0x0F). It is the standard for memory addresses and I2C device IDs. - Unsigned Decimal (uint8_t / uint16_t): Use this column when dealing with absolute magnitudes that cannot be negative, such as PWM duty cycles (0-255 on 8-bit, 0-65535 on 16-bit), ADC raw readings, or memory array indices.
- Signed Decimal (int8_t / int16_t): Use this column exclusively when your data represents physical quantities that cross zero, such as temperature sensor readings (e.g., from a DS18B20) or signed accelerometer axes. This column uses Two's Complement notation.
>>) performs an arithmetic shift (preserving the sign bit), while shifting an unsigned integer performs a logical shift (padding with zeros). Always cast to uint8_t or uint16_t before masking.
The Complete 4-Bit Nibble and 8-Bit Boundary Chart
The following tables provide the mathematically complete 4-bit nibble chart (all 16 possible states) and the complete 8-bit boundary and power-of-two chart. These are the exact rows queried by 99% of embedded developers configuring hardware registers. For intermediate 8-bit values (e.g., 45), simply combine the hex/binary values of the 4-bit nibbles (45 = 32 + 8 + 4 + 1 = 0010 1101).
Complete 4-Bit Nibble Chart (0-15)
| Binary (4-bit) | Hex | Unsigned Dec (uint8_t) | Signed Dec (int8_t) |
|---|---|---|---|
| 0000 | 0x0 | 0 | 0 |
| 0001 | 0x1 | 1 | 1 |
| 0010 | 0x2 | 2 | 2 |
| 0011 | 0x3 | 3 | 3 |
| 0100 | 0x4 | 4 | 4 |
| 0101 | 0x5 | 5 | 5 |
| 0110 | 0x6 | 6 | 6 |
| 0111 | 0x7 | 7 | 7 |
| 1000 | 0x8 | 8 | -8 |
| 1001 | 0x9 | 9 | -7 |
| 1010 | 0xA | 10 | -6 |
| 1011 | 0xB | 11 | -5 |
| 1100 | 0xC | 12 | -4 |
| 1101 | 0xD | 13 | -3 |
| 1110 | 0xE | 14 | -2 |
| 1111 | 0xF | 15 | -1 |
Complete 8-Bit Boundary, Mask, and Power-of-Two Chart
| Binary (8-bit) | Hex | Unsigned (uint8_t) | Signed (int8_t) | Common Register Use Case |
|---|---|---|---|---|
| 00000000 | 0x00 | 0 | 0 | Clear all pins / Reset register |
| 00000001 | 0x01 | 1 | 1 | Bit 0 mask (Pin 0) |
| 00000010 | 0x02 | 2 | 2 | Bit 1 mask (Pin 1) |
| 00000100 | 0x04 | 4 | 4 | Bit 2 mask (Pin 2) |
| 00001000 | 0x08 | 8 | 8 | Bit 3 mask (Pin 3) |
| 00010000 | 0x10 | 16 | 16 | Bit 4 mask (Pin 4) |
| 00100000 | 0x20 | 32 | 32 | Bit 5 mask (Pin 5 / SPI MOSI) |
| 01000000 | 0x40 | 64 | 64 | Bit 6 mask (Pin 6) |
| 01111111 | 0x7F | 127 | 127 | Max positive signed 8-bit int |
| 10000000 | 0x80 | 128 | -128 | Bit 7 mask / Min signed 8-bit int |
| 11110000 | 0xF0 | 240 | -16 | Upper nibble mask |
| 11111110 | 0xFE | 254 | -2 | Clear Bit 0 mask (~0x01) |
| 11111111 | 0xFF | 255 | -1 | Set all pins / Max unsigned 8-bit |
How Bit-Width Truncation (Derating) Modifies Base Values
In wire sizing charts, 'derating' reduces ampacity based on temperature. In binary and decimal charts for embedded systems, the equivalent concept is bit-width truncation and overflow. When you force a base-10 value into a register smaller than its binary footprint requires, the most significant bits (MSBs) are silently discarded. This modifies your base value in ways that can brick a peripheral if you aren't paying attention.
Here is how truncation modifies your decimal values when mapped to hardware:
- The 8-Bit Overflow Wrap: If you attempt to write
256(Binary:1 0000 0000) to an 8-bit register like Arduino'sOCR0A(Timer0 Output Compare), the 9th bit is truncated. The register receives0000 0000(Decimal 0). Your PWM duty cycle drops to zero instead of maxing out. - The Signed Crossover: If you assign the unsigned decimal
200to anint8_tvariable, the binary pattern11001000is stored. When read back as a signed decimal, the Two's Complement interpretation yields-56. This is the #1 cause of 'ghost' negative temperature readings in DIY sensor arrays. - The ESP32 32-Bit GPIO Trap: The ESP32 Technical Reference Manual defines
GPIO_OUT_REGas a 32-bit register. However, GPIO pins 32-39 are routed through a completely different register (GPIO_OUT1_REG). If you try to shift a bit past position 31 (1 << 32), it overflows the 32-bit integer boundary, resulting in a mask of0x00000000. The pin will not toggle.
Decision Path: Picking the Right Data Type and Mask
Stop guessing your variable types. Use this decision tree to terminate on the exact C++ data type and bitwise operation required for your specific hardware task. This framework aligns with standard Arduino bitwise math practices and modern ESP-IDF conventions.
| If your hardware task is... | And your value range is... | THEN use this Data Type | AND apply this Bitwise Mask / Operation |
|---|---|---|---|
| Reading a single digital GPIO pin state | 0 or 1 | uint8_t |
state = (REG >> PIN_NUM) & 0x01; |
| Toggling a specific pin in a port register without affecting others | N/A (Bit manipulation) | uint8_t (or uint32_t for ESP32) |
REG ^= (1 << PIN_NUM); (XOR toggle) |
| Reading a standard 10-bit ADC (e.g., Arduino Uno A0) | 0 to 1023 | uint16_t |
value = ADC_REG & 0x03FF; (Masks to 10 bits) |
| Extracting the upper nibble of an I2C sensor byte | 0 to 15 | uint8_t |
upper = (raw_byte >> 4) & 0x0F; |
| Storing a temperature reading that can drop below 0°C | -128 to +127 | int8_t |
None required (rely on Two's Complement hardware math) |
| Setting an 8-bit PWM duty cycle to exactly 50% | 0 to 255 | uint8_t |
duty = 0x7F; (Decimal 127) |
What This Chart Cannot Tell You
While this binary and decimal chart gives you the exact mathematical mappings for integer registers, it has three blind spots that you must account for in your firmware architecture:
- Endianness (Byte Order): This chart assumes you are looking at a single byte or a logically contiguous integer. When you transmit a 16-bit decimal value (like
1024, or0x0400) over SPI or I2C, the hardware will split it into two 8-bit bytes. An ESP32 (Little-Endian) will send the0x00byte first, while a Motorola-based system or certain network protocols (Big-Endian) will send0x04first. The chart cannot tell you which byte hits the wire first; you must check your microcontroller's datasheet. - IEEE 754 Floating-Point Layouts: If you are dealing with
floatordoublevariables (e.g.,3.14), base-2 integer charts are useless. Floating-point numbers use the IEEE 754 standard, which splits the 32 bits into a sign bit, an 8-bit exponent, and a 23-bit mantissa. The decimal1.0is not0x01in memory; it is0x3F800000. Never use bitwise masks on floating-point variables. - Hardware Pin Mapping vs. Logical Bit Position: A binary '1' in Bit 3 of a register does not universally mean 'Physical Pin 3 on the silkscreen'. On an Arduino Nano, Bit 3 of
PORTDis physical Pin 3. But on an ESP32-WROOM-32, the GPIO matrix routes logical register bits to physical pads dynamically. Always cross-reference your binary mask with the specific dev board's pinout schematic, not just the silicon datasheet.
By anchoring your firmware logic to the exact boundaries in these tables and respecting the truncation limits of your target registers, you eliminate the off-by-one errors and ghost states that plague most DIY embedded projects.






