A signed binary integer is a digital number format that uses a dedicated bit—usually the most significant bit (MSB)—to represent negative values, allowing microcontrollers to process data that crosses below zero. In a real circuit, enforcing the correct signed data type is the difference between accurately reading a reverse battery current and mistakenly commanding a motor driver to output maximum PWM duty cycle. Beginners commonly confuse signed integers with unsigned integers (which only count up from zero) or sign-magnitude formats, but almost all modern embedded systems—including the ARM Cortex-M in ESP32s and the AVR in Arduinos—rely exclusively on Two's Complement signed integers to handle bipolar math.
The Mechanics of Two's Complement (Numeric Example)
To understand why signed binary integers matter on the bench, you have to look at how the silicon actually counts. In an unsigned 8-bit integer, the MSB represents a value of +128. In a signed 8-bit integer using Two's Complement, that exact same MSB represents a weight of -128. This elegant mathematical trick allows microprocessors to use the exact same addition and subtraction logic circuits for both positive and negative numbers, saving silicon die space and clock cycles.
Let's walk through a worked numeric example using an 8-bit signed integer to represent -42, a value you might encounter when reading a sub-zero temperature sensor or a reverse-current shunt.
- Start with the positive binary: +42 in 8-bit binary is
0010 1010. - Invert the bits (One's Complement): Flip every 1 to 0 and 0 to 1, yielding
1101 0101. - Add 1 (Two's Complement):
1101 0101+0000 0001=1101 0110.
The final binary 1101 0110 (Hex 0xD6) is how the microcontroller stores -42. If you verify the math by applying the signed bit weights: (-128) + 64 + 16 + 4 + 2 = -42. The system works perfectly. However, if your C++ code accidentally declares this variable as an uint8_t (unsigned), the microcontroller reads that exact same 1101 0110 pattern as +214. According to All About Circuits, this bit-level misinterpretation is one of the most common root causes of logic faults in embedded DSP (Digital Signal Processing) code.
Where You Meet Signed Binary Integers in Practice
You will encounter signed binary integers whenever a physical parameter crosses a zero-threshold. If you are building any of the following systems, your data buffers must be explicitly typed as signed (int16_t, int32_t):
- Bidirectional Current Sensing: I2C shunt monitors like the INA219 output a 16-bit signed integer for shunt voltage, where 1 LSB equals 10µV. A positive value indicates charging; a negative value indicates discharging. Similarly, Hall-effect sensors like the ACS712 output an analog voltage centered at VCC/2, which your ADC must convert to a signed integer to represent reverse current flow.
- Sub-Zero Temperature Monitoring: The ubiquitous DS18B20 digital temperature sensor outputs a 12-bit signed integer. The MSB dictates the sign, and the remaining bits provide a resolution of 0.0625°C per LSB. If you cast this to an unsigned integer, a reading of -10°C will display as a nonsensical +4086.
- AC Grid and Motor Control: Field Oriented Control (FOC) algorithms for BLDC motors rely heavily on signed 32-bit integers (
int32_t) for Clarke and Park transforms. The AC phase currents constantly swing positive and negative relative to the neutral point. Grid-tied solar inverters sampling the 120V/240V AC mains via voltage transformers also require signed arrays to calculate true RMS and power factor.
The Cost of the Wrong Data Type in Real Circuits
What changes in a real installation when you mix up signed and unsigned integers? In low-power sensor logging, a data type mismatch just ruins your CSV file. In power electronics and motor control, it destroys hardware.
uint16_t), the -15 is interpreted as 65,521. If this value is fed directly into a PID control loop or a PWM duty-cycle register without bounds-checking, the microcontroller will instantly command 100% duty cycle to the IGBT gate drivers in an attempt to 'correct' the massive perceived error. The motor will slam to maximum speed in the wrong direction, likely tripping the main overcurrent breaker, melting the terminal lugs, or violently shorting the DC bus.
Always use fixed-width signed types from the <stdint.h> library—such as int16_t or int32_t—rather than generic int declarations, which can change bit-width depending on whether you are compiling for an 8-bit Arduino Uno or a 32-bit ESP32.
Quick Reference: Bit-Depth Ranges and C++ Data Types
Use this table to select the correct variable size when configuring your ADC buffers or sensor libraries. These ranges assume standard Two's Complement architecture.
| Bit Width | Unsigned Range (uint) | Signed Range (int) | Standard C++ Type | Common Embedded Use Case |
|---|---|---|---|---|
| 8-bit | 0 to 255 | -128 to +127 | int8_t / uint8_t |
PWM duty cycles, basic status flags |
| 16-bit | 0 to 65,535 | -32,768 to +32,767 | int16_t / uint16_t |
INA219 current, DS18B20 temp, standard ADC |
| 32-bit | 0 to 4,294,967,295 | -2.14B to +2.14B | int32_t / uint32_t |
FOC motor math, energy accumulation (Wh) |
Frequently Asked Questions
Why do microcontrollers use Two's Complement instead of Sign-Magnitude for signed binary integers?
Sign-magnitude dedicates the MSB purely as a positive/negative flag while keeping the remaining bits identical for the absolute value (e.g., +5 is 0101, -5 is 1101). While this is easier for humans to read, it creates a massive hardware inefficiency: it results in two distinct binary representations for zero (0000 and 1000) and requires entirely separate, complex subtraction logic circuits. Two's Complement guarantees a single representation for zero and allows the ALU (Arithmetic Logic Unit) to use the exact same adder circuits for both addition and subtraction, which is critical for high-speed DSP in motor control.
How do I fix an Arduino ADC reading 65535 when measuring negative current?
This happens when you subtract a larger unsigned ADC value from a smaller one, causing an underflow that wraps around to the maximum unsigned limit. To fix it, cast your ADC readings to a signed 16-bit integer before doing the math. For example, instead of int raw = analogRead(A0) - 512;, use int16_t raw = (int16_t)analogRead(A0) - 512;. This forces the compiler to treat the 10-bit ADC result as a signed number, allowing the result to properly cross below zero into negative values when current reverses.
Does the ESP32 ADC natively output signed binary integers for AC waveforms?
No. According to the official Espressif ESP-IDF documentation, the ESP32's SAR ADC natively outputs an unsigned 12-bit integer ranging from 0 to 4095. If you are sampling an AC waveform (like a 12V AC transformer output biased to 1.65V), the ADC will output values hovering around 2048. To get a signed binary integer representing the actual AC wave, you must manually subtract the DC bias offset in your code: int16_t ac_sample = adc_raw - 2048;. Furthermore, because the ESP32 ADC is notoriously non-linear near the 0 and 4095 rails, you should design your analog front-end to keep the AC peak-to-peak swing within the 10% to 90% range of the ADC's voltage window.






