Representing decimals in binary is the process of converting base-10 numbers—including fractional values—into base-2 format using negative powers of two for the fractional bits and standard positional weighting for the integer bits. In physical circuits and embedded systems, this representation dictates the required bit-width of your Analog-to-Digital Converters (ADCs), the memory footprint in your microcontroller registers, and the clock-cycle cost of your math operations. Makers and students commonly confuse pure binary fractional representation with Binary Coded Decimal (BCD), or mistakenly assume an 8-bit microcontroller handles floating-point math natively without a severe performance penalty.
The Core Mechanism: Integer vs. Fractional Bits
To understand how to represent decimals in binary, you must split the number at the radix point (the binary equivalent of a decimal point). The integer portion uses standard positive powers of two, while the fractional portion uses negative powers of two. The hardware doesn't actually store a "dot"; it stores a continuous string of 1s and 0s, and the radix point is implied by the data format you choose.
| Position | 2³ | 2² | 2¹ | 2⁰ | Radix Point | 2⁻¹ | 2⁻² | 2⁻³ |
|---|---|---|---|---|---|---|---|---|
| Weight | 8 | 4 | 2 | 1 | . | 0.5 | 0.25 | 0.125 |
Worked Numeric Example: Converting 13.625
Let's convert the base-10 decimal 13.625 into binary. We handle the integer and fractional parts separately.
1. The Integer Part (13):
Find the largest powers of 2 that fit into 13.
13 - 8 (2³) = 5
5 - 4 (2²) = 1
1 - 1 (2⁰) = 0
Binary integer: 1101
2. The Fractional Part (0.625):
Multiply the fraction by 2 repeatedly. If the result is ≥ 1, record a '1' and subtract 1. If < 1, record a '0'.
0.625 × 2 = 1.25 → Record 1, keep 0.25
0.25 × 2 = 0.50 → Record 0, keep 0.50
0.50 × 2 = 1.00 → Record 1, keep 0.00 (Stop)
Binary fraction: .101
Combined Result: 13.625 in base-10 is exactly 1101.101 in base-2.
Fixed-Point vs. Floating-Point (IEEE 754) in Microcontrollers
Hardware registers don't have radix points. To store 1101.101 in a microcontroller, you must choose a data format. The two primary methods are fixed-point and floating-point, and choosing the wrong one will either destroy your math precision or tank your CPU performance.
Fixed-Point Math (Q-Format):
In fixed-point, you dedicate a specific number of bits to the integer and a specific number to the fraction. For example, in a 16-bit Q8.8 format, the top 8 bits are the integer and the bottom 8 bits are the fraction. You scale your real-world values by a constant factor (e.g., multiplying voltage by 256 before storing it). According to Texas Instruments' application notes on fixed-point math, this method is vastly faster on processors without dedicated math hardware because it relies entirely on simple bit-shifting and integer addition.
Floating-Point (IEEE 754):
The float data type in C/C++ uses the IEEE 754 standard. A 32-bit single-precision float allocates 1 bit for the sign, 8 bits for the exponent, and 23 bits for the mantissa (the significant digits). This allows you to represent incredibly large or incredibly small numbers, but the bit-shifting required to align mantissas during addition is computationally heavy.
float multiplication can take over 100 clock cycles via software emulation. If you are doing high-frequency PID control loops on an 8-bit AVR, use fixed-point integer math. The ESP32, however, includes a hardware FPU, making IEEE 754 float operations execute in just a few cycles.
Where You Meet This in Practice
Theory becomes physical reality when you interface with sensors and actuators. Here is where binary decimal representation directly impacts your circuit design and code.
1. ADC Resolution and Voltage Steps
When an Analog-to-Digital Converter reads a sensor voltage, it maps that continuous analog voltage to discrete binary steps. If you are using the ESP32's 12-bit ADC with a 3.3V reference, the hardware returns an integer from 0 to 4095. To get the decimal voltage back, you multiply the raw binary integer by the step size. Step Size = 3.3V / 4095 = 0.000805V per bit. If your application requires resolving changes smaller than 0.8mV, a 12-bit ADC is physically incapable of providing that binary resolution, and you must upgrade to a 16-bit or 24-bit external ADC like the ADS1115.
2. PWM Duty Cycle Mapping
When you use analogWrite() or the ESP32's LEDC peripheral to drive a motor or dim an LED, you are passing a binary integer that represents a fractional duty cycle. On an 8-bit PWM timer, a value of 128 represents exactly 50% (128/255). If you need a precise decimal duty cycle of 33.3%, you must calculate the binary integer equivalent (0.333 × 255 ≈ 85) and accept the 0.1% fractional truncation error inherent in the 8-bit binary representation.
3. Sensor Data Telemetry
When transmitting sensor data over MQTT or LoRa, bandwidth is expensive. Sending a 32-bit IEEE 754 float for a temperature reading wastes bytes. By applying fixed-point math (e.g., multiplying 23.45°C by 100 to send the 16-bit integer 2345), you halve the payload size while retaining exact decimal precision to two places. As noted in Analog Devices' data conversion guides, managing the binary representation of your data is just as critical as the physical signal conditioning.
Frequently Asked Questions
How do you represent negative decimals in binary?
Negative decimals are represented using Two's Complement. You take the binary representation of the positive number, invert all the bits (change 1s to 0s and 0s to 1s), and then add 1 to the least significant bit. For fractional numbers in fixed-point formats, the radix point stays in the exact same implied position; only the integer arithmetic rules change to accommodate the sign bit at the most significant position.
What is the difference between pure binary and Binary Coded Decimal (BCD)?
Pure binary converts the entire base-10 number into base-2 using positional weighting (e.g., 13 is 1101). Binary Coded Decimal (BCD) encodes each individual base-10 digit into its own 4-bit binary nibble. In BCD, the number 13 is represented as 0001 0011 (1 and 3). BCD is highly inefficient for math and memory, but it is still used in digital clocks, older digital multimeters, and financial calculators because it completely avoids the rounding errors inherent in converting binary fractions back to base-10 displays.
Why does my ESP32 serial monitor print long trailing decimals like 3.1400001?
This is a fundamental limitation of the IEEE 754 floating-point standard, not a bug in your ESP32. Many base-10 fractions (like 0.1 or 0.14) cannot be represented exactly as a finite sum of negative powers of two, much like how 1/3 cannot be written exactly in base-10 decimals (0.333...). The microcontroller stores the closest possible binary approximation. When the Serial.print() function converts that 32-bit binary float back to base-10 ASCII characters for your monitor, the tiny approximation error becomes visible. Use Serial.print(value, 2) to force the display to round to two decimal places.
How many bits do I need to represent a specific decimal resolution?
To find the required bit-width (n) for a specific resolution over a given range, use the formula: n = log₂(Range / Resolution), rounded up to the nearest whole number. For example, if you need to measure a 0-5V range with a resolution of 0.001V (1mV), you need 5 / 0.001 = 5000 discrete steps. Since 2¹² = 4096 (not enough) and 2¹³ = 8192 (enough), you need a minimum of a 13-bit ADC. In practice, you would select a standard 16-bit ADC to provide headroom for noise and oversampling.






