A signed number is a numeric representation that reserves the most significant bit (MSB) to indicate polarity, allowing digital systems to process and compute values below zero. In the physical world of electrical engineering, voltages swing negative, motors spin in reverse, and AC currents alternate direction. However, microcontrollers and digital logic only understand binary ones and zeros. To bridge this gap, digital systems use signed number formats—almost universally two's complement—to map negative physical realities into binary registers. If you are reading bipolar sensors, writing PID control loops, or configuring digital-to-analog converters (DACs), misunderstanding how your hardware handles signed data will result in catastrophic wrap-around errors, runaway motors, and bricked prototypes.

The Binary Mechanics: Two's Complement Ranges

Unlike human mathematics, where we simply slap a minus sign in front of a digit, binary systems must encode the sign within a fixed number of bits. The industry standard for this is two's complement. In this system, the MSB acts as the sign bit: a 0 means positive, and a 1 means negative. Crucially, this creates an asymmetrical range. An 8-bit signed integer can hold values from -128 to +127, not -127 to +127, because zero occupies one of the positive slots.

Before writing firmware for a new sensor or motor driver, you must verify the bit-width and signedness of its data registers. The table below outlines the exact boundaries you will encounter across common embedded hardware.

Bit-Width Unsigned Range Signed Range (Two's Comp) Hex for -1 Typical Hardware Application
8-bit 0 to 255 -128 to +127 0xFF Basic PWM duty cycles, 8-bit GPIO port states
10-bit 0 to 1023 -512 to +511 0x3FF STM32 internal ADCs (differential input mode)
12-bit 0 to 4095 -2048 to +2047 0xFFF Bipolar audio DACs, MPL3115A2 pressure sensors
16-bit 0 to 65535 -32768 to +32767 0xFFFF External ADCs (ADS1115), Modbus holding registers
32-bit 0 to 4,294,967,295 -2,147,483,648 to +2,147,483,647 0xFFFFFFFF 32-bit PID error accumulators, DSP math cores
Bench Tip: When reading a 16-bit signed sensor over I2C or SPI, the data often arrives as two 8-bit bytes (MSB first). If you simply stitch them together into an unsigned 16-bit integer in C/C++, a negative value like 0xFFFF (-1) will be read as 65535. Always cast the combined register to a signed integer type (e.g., int16_t) before applying your scaling math.

Worked Numeric Example: Reading Bipolar Current with a 16-Bit ADC

To see what a signed number changes in a real circuit, let's look at a bidirectional current sensing application using the ubiquitous Texas Instruments ADS1115 16-bit ADC. Suppose you are monitoring a DC motor. When the motor drives forward, current flows through the shunt resistor in one direction, creating a positive voltage drop. When the motor brakes or reverses, current flows backward, creating a negative voltage drop.

The Setup:

  • ADC: ADS1115 configured for a Full Scale Range (FSR) of ±4.096V.
  • Shunt Resistor: 0.1Ω.
  • Measured Physical Current: -0.75A (motor in reverse/regenerative braking).
  • Voltage across shunt: -0.075V (-75mV).

The Math:
The ADS1115 outputs a 16-bit signed number. Because it is signed, the maximum positive code is 32,767, representing +4.096V. The resolution (weight of the Least Significant Bit, or LSB) is calculated as:

Resolution = 4.096V / 32768 = 0.125mV per LSB

Now, we convert our measured -75mV into the digital code:

ADC Code = -75mV / 0.125mV = -600

In binary and hexadecimal, -600 is represented as 0xFD88. If your microcontroller reads the I2C bus and receives the bytes 0xFD and 0x88, it stitches them into 0xFD88. If your firmware treats this as an unsigned 16-bit integer, the microcontroller interprets 0xFD88 as 64,904. Your code will multiply 64,904 by 0.125mV, concluding the shunt voltage is +8.11V and the motor is drawing 81 Amps forward. The physical reality is a gentle 0.75A reverse current, but your unsigned math just triggered a catastrophic overcurrent fault shutdown.

By declaring the variable as an int16_t (signed), the compiler automatically recognizes the MSB is high, applies two's complement logic, and correctly resolves the value to -600, allowing your code to accurately command the H-bridge to handle the regenerative braking.

Where You Meet Signed Numbers in Practice

You cannot avoid signed numbers if your circuit interacts with alternating physical phenomena or closed-loop control. Here is where they dictate system behavior:

1. AC Mains Monitoring and Audio Processing

Grid voltage and audio waveforms are inherently bipolar, swinging above and below a zero-crossing reference. When using a current transformer (CT) clamp to monitor a 120V/240V AC branch circuit, the ADC must sample both the positive and negative halves of the 50/60Hz sine wave. If you bias the signal to a mid-rail voltage (e.g., 1.65V) to use an unsigned ADC, you must subtract that bias in software, effectively creating a signed number in your DSP math to calculate true RMS power.

2. PID Control Loops and Integrator Windup

In temperature controllers or drone flight stabilizers, the 'Error' term is the Setpoint minus the Process Variable. If your drone tilts past the target angle, the error becomes negative. The Integral (I) term accumulates these errors over time. If you use unsigned integers for the I-term accumulator, a negative error will cause an underflow wrap-around, spiking the accumulator to its maximum positive value. This phenomenon, known as integrator windup, will cause the motors to max out in the wrong direction. Always use 32-bit signed integers (int32_t) or floating-point variables for PID accumulators.

3. Rotary Encoders and Position Tracking

Quadrature encoders output pulses that a microcontroller counts to determine shaft position. If the shaft reverses, the counter must decrement. Hardware quadrature decoder peripherals in modern MCUs (like the STM32 TIM encoders) natively handle signed counting, allowing the position register to roll seamlessly from 0 to -1 as the motor reverses.

Common Confusions and Catastrophic Wrap-Arounds

Even experienced hobbyists trip over the nuances of digital representation. According to foundational digital logic principles outlined by resources like All About Circuits, the two most common pitfalls are:

Hazard: Sign-Magnitude vs. Two's Complement
Humans naturally think in 'sign-magnitude' (e.g., -5 is just 5 with a minus sign attached). Some older or highly specialized protocols use a dedicated sign bit followed by the absolute value. However, 99% of modern microcontrollers and ADCs use two's complement. In two's complement, 1000 0000 is -128, not -0. Assuming sign-magnitude when reading a two's complement register will completely invert and corrupt your negative scaling math.

Unsigned Overflow vs. Signed Overflow

What people commonly confuse signed numbers with is the concept of 'overflow limits'. In an 8-bit unsigned system, adding 1 to 255 causes an overflow, wrapping around to 0. This is predictable and sometimes useful for timers.

In an 8-bit signed system, adding 1 to the maximum positive value (+127, or 0x7F) flips the sign bit. The result is 0x80, which is -128. If your firmware is tracking the position of a stepper motor using an 8-bit signed variable, and the motor takes one step past +127, the software will suddenly believe the motor has teleported to the extreme negative limit. This signed overflow is a primary cause of 'fly-away' bugs in robotics, where a machine violently reverses direction due to a math boundary being crossed.

Frequently Asked Questions

Q: Can I just use floating-point numbers instead of signed integers?
A: You can, and float handles negative values natively. However, many 8-bit and entry-level 32-bit MCUs lack a hardware Floating Point Unit (FPU). Performing float math on these chips requires expensive software emulation, consuming critical clock cycles in high-speed sampling or motor commutation loops. Fixed-point signed integer math is vastly faster and more deterministic.

Q: How do I convert a negative signed number to positive in C/C++?
A: Use the standard abs() function from <stdlib.h> (or std::abs() in C++). Do not attempt to manually flip bits unless you are writing low-level DSP assembly; the compiler will optimize abs() into the most efficient bitwise operations for your specific architecture.

Q: Why does my multimeter read negative voltage, but my Arduino reads 0?
A: Standard microcontroller GPIO and internal ADC pins are strictly unipolar (0V to VCC). They cannot read negative voltages; doing so will clamp the internal protection diodes and potentially damage the silicon. To read negative physical voltages, you must use an external differential ADC (like the ADS1115) or offset the signal with an op-amp summing circuit before it reaches the microcontroller.