Binary subtraction is the mathematical process of deducting one base-2 number from another using specific bitwise borrowing rules or, more commonly in modern hardware, two's complement addition. When you write int x = a - b; on an ESP32 or Arduino, the microcontroller doesn't actually 'subtract' in the way you learned in grade school; it manipulates logic gates to add a negative. Understanding the rules of binary subtraction is critical for debugging integer underflow, optimizing DSP algorithms, and designing custom FPGA logic.
The Core Rules of Binary Subtraction (Base-2 Borrowing)
At the most fundamental level, base-2 borrowing follows a strict truth table. If you are designing a dedicated half-subtractor or full-subtractor circuit out of discrete 7400-series logic gates, these are the exact rules your silicon must execute.
| Minuend (A) | Subtrahend (B) | Difference (D) | Borrow Out (Bout) |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 |
Worked Numeric Example: Standard Borrowing
Let's subtract 6 from 13. In decimal, this is 13 - 6 = 7. In binary, 13 is 1101 and 6 is 0110.
- Bit 0 (1s place): 1 - 0 = 1. (No borrow)
- Bit 1 (2s place): 0 - 1. We must borrow from Bit 2. Bit 1 becomes 2 (in decimal concept), so 2 - 1 = 1. (Borrow generated)
- Bit 2 (4s place): The original 1 was borrowed from, leaving 0. We need to calculate 0 - 1. We borrow from Bit 3. 2 - 1 = 1. (Borrow generated)
- Bit 3 (8s place): The original 1 was borrowed from, leaving 0. 0 - 0 = 0.
The result is 0111, which is exactly 7 in decimal. While this makes sense on paper, building a cascading borrow chain in hardware creates severe propagation delay. This leads us to how modern silicon actually handles the math.
What People Commonly Confuse: Borrowing vs. Two's Complement
The most common mistake hobbyists and junior firmware engineers make is assuming the CPU uses the borrowing method shown above. In reality, almost all modern Arithmetic Logic Units (ALUs) use two's complement addition to perform subtraction.
00000000 yields 11111111, which the ALU interprets as -1.
To subtract B from A using two's complement, the hardware does three things:
- Inverts all bits of B (using simple NOT gates).
- Adds 1 to the inverted B (by asserting the Carry-In pin on the adder).
- Adds this modified B to A using a standard binary adder.
Worked Example (13 - 6 via Two's Complement):
A = 1101 (13)
B = 0110 (6)
Invert B: 1001
Add 1 to inverted B: 1010 (This is the two's complement representation of -6).
Add A + (-B): 1101 + 1010 = 10111.
Discarding the 5th carry-out bit leaves 0111 (7). No complex borrow-routing required.
Where You Meet This in Practice: ALUs and Microcontrollers
Understanding these rules changes how you evaluate hardware architecture and write bare-metal code. In a real circuit, using two's complement instead of a dedicated subtractor drastically reduces the silicon area and gate count. According to the Espressif ESP32 Technical Reference Manual, the Xtensa LX6 ALU relies on shared adder circuitry for both addition and subtraction, toggling a single control line to invert the B-input bits and set the carry-in high.
When you are programming FPGAs in Verilog or VHDL, you can explicitly instantiate a full-subtractor, but synthesis tools like Xilinx Vivado or Intel Quartus will almost always optimize assign diff = a - b; into an adder with inverted inputs. If you are studying digital logic from the ground up, the Nand2Tetris project provides an excellent framework for building an ALU that switches between addition and subtraction using a single XOR gate array and a control bit.
Debugging Binary Math Errors in Embedded C/C++
Because microcontrollers execute binary subtraction strictly by the bits, ignoring human concepts of 'negative' unless explicitly told otherwise, math errors frequently destroy hardware or crash firmware.
The Unsigned Underflow Hazard
If you subtract a larger number from a smaller number using an unsigned integer type, the ALU doesn't throw an error. It simply wraps around.
uint8_t position_target = 5;
uint8_t position_current = 10;
uint8_t error = position_target - position_current;
// error is NOT -5. It is 251.
int8_t, int16_t) for any variable that represents a difference or error margin.
For deeper insights into how these bitwise operations map to C/C++ data types, the All About Circuits digital logic textbook offers comprehensive chapters on binary arithmetic and hardware mapping.
Frequently Asked Questions
What are the rules for binary subtraction with negative numbers?
In standard hardware, negative numbers don't have a separate subtraction rule; they are already stored in two's complement format. To subtract a negative number (e.g., A - (-B)), the ALU takes the two's complement of the already-negative B (which turns it back into a positive B) and adds it to A. Essentially, subtracting a negative number becomes standard binary addition.
How do you subtract binary numbers with different lengths?
Before subtraction, the shorter binary number must be padded with leading zeros to match the bit-width of the longer number. For example, if subtracting 101 (5) from 11010 (26), you pad the subtrahend to 00101. If you are using signed numbers, you must use 'sign extension' instead of zero-padding—copying the most significant bit (the sign bit) into the new leading positions to preserve the negative value.
Why do computers use two's complement instead of standard binary subtraction rules?
Computers use two's complement because it allows the ALU to use the exact same physical adder circuitry for both addition and subtraction, saving millions of transistors and reducing propagation delay. Furthermore, two's complement ensures there is only one representation for zero (00000000), whereas older systems like 'one's complement' or 'sign-magnitude' suffered from having both a positive zero and a negative zero, which complicated logic branching and equality checks.
How does a full subtractor circuit work in digital logic?
A full subtractor is a combinational logic circuit that performs subtraction on three bits: the Minuend (A), the Subtrahend (B), and a Borrow-In (Bin) from a previous less-significant stage. It produces two outputs: the Difference (D) and the Borrow-Out (Bout). The Difference is calculated using two cascaded XOR gates (A XOR B XOR Bin), while the Borrow-Out is generated using a combination of AND, OR, and NOT gates to detect if A is smaller than the combined value of B and Bin.






