The maximum decimal value of an 8-bit unsigned binary register is 255. For a 16-bit unsigned register, it is 65,535, and for a 32-bit unsigned register, it is 4,294,967,295. When you are configuring PWM resolution on an ESP32, reading a 12-bit ADC on an Arduino, or parsing I2C sensor data, guessing these boundaries leads to integer overflow, clipped signals, and bricked logic loops. You need exact numbers, not abstract theory.
The Master Binary and Decimal Table (ISO/IEC 9899 C11 Standard)
The following table maps binary bit-widths to their maximum decimal and hexadecimal values, anchored to the exact-width integer types defined in the ISO/IEC 9899 C11 standard (<stdint.h>). This is the native data typing used by the GCC compiler under the hood of both the Arduino IDE and Espressif ESP-IDF.
| Bit Width | Binary Representation (Max) | Decimal (Unsigned) | Decimal (Signed Range) | Hex Max | Standard C/C++ Type |
|---|---|---|---|---|---|
| 4-bit (Nibble) | 1111 | 15 | -8 to +7 | 0x0F | No native C type (use bitfields) |
| 7-bit (I2C Addr) | 111 1111 | 127 | -64 to +63 | 0x7F | Protocol specific (mask with 0x7F) |
| 8-bit (Byte) | 1111 1111 | 255 | -128 to +127 | 0xFF | uint8_t / int8_t |
| 10-bit (PWM/ADC) | 11 1111 1111 | 1,023 | -512 to +511 | 0x3FF | uint16_t (masked) |
| 12-bit (ADC) | 1111 1111 1111 | 4,095 | -2,048 to +2,047 | 0xFFF | uint16_t (masked) |
| 16-bit (Word) | 1111 1111 1111 1111 | 65,535 | -32,768 to +32,767 | 0xFFFF | uint16_t / int16_t |
| 32-bit (DWord) | (8 groups of 1111) | 4,294,967,295 | -2,147,483,648 to +2,147,483,647 | 0xFFFFFFFF | uint32_t / int32_t |
Which Column Applies to Your Application (and How Modifiers Shift the Base)
Choosing the correct column depends entirely on the physical reality of the sensor or actuator you are interfacing with, combined with the microcontroller's hardware peripherals.
When to use the Unsigned Column: Use unsigned types (uint8_t, uint16_t) for values that physically cannot drop below zero. This includes ADC raw readings (a photodiode cannot output negative light), PWM duty cycles, I2C bus addresses, and RGB LED hex color codes. Using an unsigned type gives you double the positive headroom.
When to use the Signed Column: Use signed types (int8_t, int16_t) when measuring bipolar physical phenomena. If you are reading the Z-axis of an MPU6050 accelerometer, or a thermocouple measuring sub-zero ambient temperatures, the value must cross zero.
How non-standard bit widths (10-bit, 12-bit, 14-bit) are handled: Microcontrollers rarely have physical 12-bit registers in main memory; they use 16-bit or 32-bit registers and mask the unused bits. For example, the ESP32’s SAR ADC natively outputs a 12-bit value (0 to 4095). However, the C++ variable holding it must be a 16-bit uint16_t. You must apply a bitwise AND mask (value & 0x0FFF) to strip away any stray high-bit noise before doing decimal math, otherwise a stray 1 in the 13th bit position will instantly multiply your decimal reading by 4,096.
What the Table Cannot Tell You: Overflow, Endianness, and Memory Alignment
A binary and decimal table gives you the mathematical boundaries, but it will not save you from the three most common firmware-level hardware bugs:
| Hidden Hazard | What the Table Misses | Real-World Failure Mode | The Fix |
|---|---|---|---|
| Integer Overflow | The table shows the max value, but not what happens when you add 1 to it. | Adding 1 to a uint8_t at 255 does not yield 256. It wraps around to 0. This causes PID control loops to violently reverse direction. |
Cast to a larger type before math: uint16_t safe_math = (uint16_t)val8 + 1; |
| Bus Endianness | The table assumes a single monolithic number, ignoring byte order over wires. | Reading a 16-bit signed temperature over I2C. The table says max is 32,767, but if you read the Low Byte before the High Byte, a reading of 25°C (0x00FA) becomes 64,000 (0xFA00). | Check the sensor datasheet. Most I2C sensors are Big-Endian; SPI flash is often Little-Endian. Use Wire.read() << 8 | Wire.read() accordingly. |
| Memory Alignment | The table ignores how 32-bit ARM CPUs fetch memory. | Packing four uint8_t variables and one uint32_t into a struct on an ESP32-S3. The CPU inserts 3 bytes of invisible 'padding' to align the 32-bit integer, breaking your raw SPI byte parsing. |
Use the __attribute__((packed)) directive on your C++ structs when mapping raw binary buffers to variables. |
Quick-Jump Bookmark Rows for Common Maker Scenarios
Keep these specific boundary values bookmarked for rapid debugging when your serial monitor outputs garbage data.
- I2C 7-Bit Addressing: Maximum decimal address is 127 (0x7F). If your scanner outputs an address of 192, you are looking at an 8-bit address that includes the Read/Write bit. Shift it right by one (
addr >> 1) to get the true 7-bit decimal value. - Arduino Uno
analogRead(): 10-bit resolution. Decimal range is 0 to 1023. If you are mapping this to a 5V reference, each decimal step equals exactly 4.88 millivolts (5.0 / 1024). - ESP32 Native
analogRead(): 12-bit resolution. Decimal range is 0 to 4095. However, the ESP32 ADC is notoriously non-linear at the extremes. Trust the middle 20% to 80% of the decimal range (approx. 800 to 3200) for accurate voltage mapping. - ESP32 LEDC PWM (v5.x IDF): The legacy 8-bit (0-255) resolution is deprecated in newer ESP-IDF versions. The hardware timer natively supports up to 14-bit resolution, yielding a decimal range of 0 to 16,383. Use
ledc_timer_config_tto explicitly setduty_resolution = LEDC_TIMER_14_BITfor smooth servo control. - Unix Epoch Time (32-bit Signed): If you are storing RTC time in a standard
int32_t, the maximum decimal value is 2,147,483,647. This translates to the 'Year 2038 Problem' (January 19, 2038, at 03:14:07 UTC). For new projects, always use a 64-bitint64_tfor timestamps.
Understanding the strict mathematical boundaries of your registers prevents the silent failures that plague embedded systems. Always declare your variables using the explicit <stdint.h> types, respect the signedness derating factor, and mask your non-standard ADC/PWM bit-widths before passing them into your control logic.






