Fractional binary is a base-2 numbering system that represents non-integer decimal values using negative powers of two to the right of a binary point. If you are designing a mixed-signal circuit, sizing a digital filter, or writing firmware for an ESP32, this mathematical concept dictates your actual measurement precision. In a real circuit, fractional binary changes how you calculate quantization error in Analog-to-Digital Converters (ADCs), determines the exact voltage steps in Digital-to-Analog Converters (DACs), and defines the granularity of your PWM duty cycle registers.
When we deal with whole numbers, binary is straightforward: 1, 2, 4, 8, 16. But the physical world is analog. Voltages, temperatures, and motor speeds rarely land on perfect integers. To bridge the digital-analog divide, microcontrollers rely on fractional binary to map continuous physical quantities into discrete digital registers. Quantization error is like trying to measure the exact height of a smooth ramp using a ruler that only has marks every half-inch; the fractional binary bits are those ruler marks, and the unavoidable gap between the mark and the actual ramp is your quantization error.
How Fractional Binary Math Actually Works
To understand the mechanics, let us look at a worked numeric example converting a mixed decimal number into its fractional binary equivalent. We will convert the decimal value 10.6875 into binary.
First, we split the number into its integer part (10) and its fractional part (0.6875). The integer 10 converts to standard binary as 1010 (8 + 2). For the fractional part, we use successive multiplication by 2, extracting the integer carry at each step:
- 0.6875 × 2 = 1.375 (Carry 1)
- 0.375 × 2 = 0.75 (Carry 0)
- 0.75 × 2 = 1.5 (Carry 1)
- 0.5 × 2 = 1.0 (Carry 1)
Reading the carries top-to-bottom gives us the fractional binary sequence .1011. Combining them, 10.6875 in decimal is exactly 1010.1011 in fractional binary.
1×23 + 0×22 + 1×21 + 0×20 . 1×2-1 + 0×2-2 + 1×2-3 + 1×2-4
8 + 0 + 2 + 0 . 0.5 + 0 + 0.125 + 0.0625 = 10.6875
This works perfectly for numbers that are exact sums of negative powers of two. However, many common decimal fractions cannot be represented perfectly in binary. Just as 1/3 becomes an endless repeating 0.333... in decimal, the decimal value 0.1 becomes an endless repeating 0.0001100110011... in fractional binary. When a microcontroller truncates this repeating sequence to fit a 12-bit or 16-bit register, it introduces a rounding error that firmware engineers must account for.
Where You Meet Fractional Binary in Practice
You will encounter fractional binary math anytime a microcontroller interacts with analog hardware or requires sub-integer timing precision.
1. ADC Resolution and the Least Significant Bit (LSB)
Consider the 12-bit ADC on an ESP32-WROOM-32 operating with a 3.3V reference. The ADC maps the 0V to 3.3V range across 4,096 discrete steps (212). The voltage weight of the lowest fractional bit (the LSB) is calculated as:
3.3V / 4096 = 0.000805V (0.805mV)
If your sensor outputs 1.500V, the ADC cannot output '1.5'. It outputs the integer 1863 (binary 11101000111). The fractional binary math tells us that 1863 × 0.805mV = 1.4997V. The 0.3mV difference is your quantization error. If you need tighter precision, you must upgrade to a 16-bit external ADC like the ADS1115, which shrinks the LSB weight to roughly 0.1mV on the same range.
2. DAC Output Voltage Truncation
When writing to a DAC, you are sending fractional binary values to set exact voltages. Suppose you are using an MCP4725 (a 12-bit I2C DAC) with a 3.3V supply, and you need exactly 1.500V out.
The required digital code is: (1.500 / 3.3) × 4096 = 1861.8181...
Because the DAC register only accepts integers, you must truncate to 1861. In binary, 1861 is 0111 0100 0101. By forcing this fractional binary truncation, your actual output voltage will be 1861 × (3.3 / 4096) = 1.4995V. You are 0.5mV short of your target.
3. PWM Duty Cycle Granularity
Pulse Width Modulation relies on fractional binary to define the 'on' time ratio. On an 8-bit AVR timer (like the ATmega328P in the Arduino Uno), the counter rolls over at 255. A 50% duty cycle requires a compare register value of 127.5. Since you can only write 127 or 128, your actual duty cycle is either 49.8% or 50.2%. Moving to a 16-bit timer gives you 65,536 steps, making the fractional binary resolution fine enough that the quantization error becomes negligible for motor control.
When calculating ADC scaling factors in firmware, avoid using standard floating-point math (e.g.,
float voltage = adc_raw * (3.3 / 4096.0);) on 8-bit microcontrollers like the Arduino Uno. The ATmega328P lacks a hardware Floating Point Unit (FPU), so the compiler uses software emulation, consuming hundreds of clock cycles per operation. Instead, use fractional binary fixed-point math by scaling up to integers: uint32_t millivolts = (adc_raw * 3300UL) / 4096;. The ESP32 and modern ARM Cortex-M4 chips have hardware FPUs, making standard floats safe to use.
Common Confusions: Fractional Binary vs. Floating-Point vs. BCD
Engineers and hobbyists frequently confuse raw fractional binary (often implemented as fixed-point math) with IEEE 754 floating-point formats and Binary-Coded Decimal. Here is how they differ in hardware and application.
| Criteria | Fractional Binary (Fixed-Point) | IEEE 754 Floating-Point | Binary-Coded Decimal (BCD) |
|---|---|---|---|
| Representation | Implicit binary point; integer scaled by 2-n | Sign bit, exponent, and mantissa | 4 bits per decimal digit (0-9) |
| Hardware Cost | Very low (uses standard ALU integer math) | High (requires dedicated FPU or heavy software) | Medium (requires decimal adjustment logic) |
| Precision Behavior | Uniform resolution across the entire range | Variable resolution (denser near zero, sparse at extremes) | Exact decimal representation (no binary rounding) |
| Best Use Case | DSP filters, PID loops, ADC/DAC scaling | Complex physics, GPS coordinates, scientific logging | Real-Time Clocks (RTCs), 7-segment displays, financial math |
According to industry DSP guidelines, fractional binary fixed-point math is overwhelmingly preferred for high-speed digital signal processing because the uniform resolution prevents the limit-cycle oscillations that floating-point rounding can introduce into IIR filters.
FAQ: Fractional Binary in Embedded Systems
Why does my ESP32 ADC read 0.1 as an endless repeating binary fraction?
This is a fundamental property of base-2 math, not a hardware defect. In the decimal system, fractions with denominators that are not factors of 10 (like 1/3) result in repeating decimals (0.333...). In binary, the only denominators that terminate cleanly are powers of 2 (1/2, 1/4, 1/8). Because 0.1 is equivalent to 1/10, and 10 is not a power of 2, its fractional binary representation is 0.0001100110011... repeating infinitely. When the ESP32's 12-bit ADC or a 32-bit float variable truncates this sequence, it results in a value like 0.10000000149. To fix this in firmware, never use direct equality checks (if (voltage == 0.1)); always use a tolerance window (if (abs(voltage - 0.1) < 0.005)).
How do I calculate the exact voltage of a fractional binary LSB?
The formula for the Least Significant Bit (LSB) voltage weight is Vref / 2n, where Vref is your reference voltage and n is the bit-depth of your converter. For a 16-bit ADC (like the ADS1115) using a 4.096V precision reference, the calculation is 4.096 / 65536 = 0.0000625V, or exactly 62.5µV per bit. Note that as Analog Devices conversion tutorials point out, the actual usable resolution is often 1 to 2 bits lower than the theoretical fractional binary limit due to thermal noise and internal reference drift on the PCB.
Should I use fractional binary fixed-point or floating-point for my PID controller?
For most motor control and temperature PID loops, fractional binary fixed-point math (specifically the 'Q-format', like Q15 which uses 1 sign bit and 15 fractional bits) is the superior choice. PID controllers rely heavily on integral and derivative accumulation. Floating-point math introduces tiny, variable rounding errors at every accumulation step, which can cause 'integral windup' drift over hours of operation. Fixed-point fractional binary math guarantees that the rounding error is strictly bounded and uniform. If you are using a 32-bit ARM Cortex-M4 or an ESP32 and your loop runs slower than 1kHz, 32-bit floats are acceptable and easier to code. If you are running a 20kHz current loop on a DSP or a resource-constrained 8-bit MCU, stick to Q-format fractional binary.






