Binary Arithmetic — Addition, Subtraction, and Two's Complement
Learn how computers perform binary arithmetic: binary addition with carry, two's complement for negative numbers, overflow detection, and how CPUs handle integer operations in...
Binary arithmetic is the foundation of all CPU operations. Understanding it helps debug integer overflow bugs, understand bit manipulation, and reason about low-level data formats.
Convert between binary, decimal, and hex with the Number Base Converter.
Binary addition
Rules: 0+0=0, 0+1=1, 1+0=1, 1+1=10 (0 carry 1)
0110 (6)
+ 0111 (7)
------
1101 (13)
Step by step:
bit 0: 0+1 = 1
bit 1: 1+1 = 0, carry 1
bit 2: 1+1+1(carry) = 1, carry 1
bit 3: 0+0+1(carry) = 1
Result: 1101 = 13 ✓
// JavaScript: bitwise addition (no carry beyond int size)
const a = 0b0110; // 6
const b = 0b0111; // 7
console.log(a + b); // 13
console.log((a + b).toString(2)); // "1101"
// Simulate 4-bit addition with overflow wrap:
function add4bit(a, b) {
return (a + b) & 0xF; // Keep only low 4 bits
}
add4bit(0b1111, 0b0001); // 1111 + 0001 = 10000, masked to 0000 = 0 (overflow)
Two’s complement: negative numbers
Computers represent negative integers using two’s complement. For an n-bit integer:
Two's complement of X = flip all bits + 1
Example: -5 in 8-bit two's complement
Step 1: 5 = 00000101
Step 2: Flip: 11111010
Step 3: Add 1: 11111011 = -5
Check: -5 + 5 = ?
11111011 (-5)
+ 00000101 (5)
----------
100000000 → the 9th bit is discarded → 00000000 = 0 ✓
// JavaScript uses 32-bit two's complement for bitwise ops:
console.log(-5 >>> 0); // 4294967291 = 0xFFFFFFFB (unsigned view)
console.log((-5 >>> 0).toString(2));
// "11111111111111111111111111111011"
// Negative number detection:
function isNegative(n) {
return (n >>> 31) === 1; // Check sign bit (MSB)
}
isNegative(-5); // true
isNegative(127); // false
Range of signed vs unsigned integers
8-bit (1 byte):
Unsigned: 0 to 255 (2^8 - 1)
Signed: -128 to 127 (-2^7 to 2^7 - 1)
16-bit (2 bytes):
Unsigned: 0 to 65,535
Signed: -32,768 to 32,767
32-bit (4 bytes):
Unsigned: 0 to 4,294,967,295
Signed: -2,147,483,648 to 2,147,483,647
64-bit (8 bytes):
Unsigned: 0 to 18,446,744,073,709,551,615
Signed: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
Integer overflow
// JavaScript numbers are 64-bit floats — no int overflow below 2^53
Number.MAX_SAFE_INTEGER // 9007199254740991 (2^53 - 1)
Number.MAX_SAFE_INTEGER + 1 // 9007199254740992
Number.MAX_SAFE_INTEGER + 2 // 9007199254740992 — same! (precision lost)
// For large integers: use BigInt
const big = 9007199254740991n + 2n; // 9007199254740993n — correct
// C/Java 32-bit int overflow (simulated):
function int32Add(a, b) {
const result = a + b;
// Convert to 32-bit signed:
return (result | 0);
}
int32Add(2147483647, 1); // -2147483648 (overflow to minimum int32)
Binary subtraction via two’s complement
Subtraction is implemented as addition of the negated value:
7 - 5 = 7 + (-5)
00000111 (7)
+ 11111011 (-5 in two's complement)
----------
100000010 → discard carry → 00000010 = 2 ✓
// CPU does subtraction this way internally:
// SUB a, b = a + (~b + 1)
function subtract(a, b) {
const negB = (~b + 1) & 0xFF; // two's complement of b (8-bit)
return (a + negB) & 0xFF; // add and mask to 8 bits
}
subtract(7, 5); // 2 ✓
subtract(5, 7); // 254 = 0xFE = -2 in 8-bit signed
Overflow detection
Signed overflow occurs when:
- Adding two positives gives a negative
- Adding two negatives gives a positive
Detection (V flag in CPU status register):
V = CarryIn XOR CarryOut (of the most significant bit)
8-bit example:
01111111 (127) + 00000001 (1):
carry into bit 7 = 0
carry out of bit 7 = 0
V = 0 XOR 0 = 0 → no overflow? Wait:
Result: 10000000 = -128 (signed overflow!)
Actually: V = (both inputs positive) AND (result negative)
Or in hardware: V = CarryIn[MSB] XOR CarryOut[MSB]
// Detect signed 8-bit overflow:
function addWithOverflow(a, b) {
const result = a + b;
const overflow = (a > 0 && b > 0 && result < 0) ||
(a < 0 && b < 0 && result > 0);
return { result: (result << 24) >> 24, overflow };
}
addWithOverflow(120, 10); // { result: -126, overflow: true }
addWithOverflow(50, 30); // { result: 80, overflow: false }
BCD: Binary-Coded Decimal
Used in financial applications and hardware timers:
Each decimal digit stored in 4 bits:
123 in BCD = 0001 0010 0011
Normal binary: 123 = 01111011
BCD: 123 = 0001 0010 0011 (12 bits for same value)
BCD wastes space but prevents floating-point rounding errors.
Used in: cash registers, clocks, calculators.
// Convert decimal to BCD:
function decimalToBCD(n) {
return n.toString().split('').map(d => parseInt(d).toString(2).padStart(4, '0')).join(' ');
}
decimalToBCD(123); // "0001 0010 0011"
Related tools
- Number Base Converter — convert between binary, decimal, hex
- Bitmask and Bitwise Operations — practical bit manipulation
- Hash Generator — binary-level hashing
Related posts
- Binary to Decimal — Convert Binary Numbers the Right Way — Binary to decimal conversion is foundational to understanding how computers stor…
- Binary to Text: How Binary Numbers Represent Characters — Binary to text conversion isn't magic — it's a lookup table. ASCII, Unicode, UTF…
- Bitmask and Bitwise Operations — Flags, Permissions, and Bit Manipulation — Bitmasks store multiple boolean flags in a single integer using bitwise AND, OR,…
Related tool
Convert between binary, octal, decimal, hexadecimal, and text (UTF-8). Handles arbitrary lengths. Per-byte and per-character views.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.