Two's Complement Binary

How computers represent signed integers using two's complement — why -x = ~x + 1, the asymmetric range, sign extension, and why this system is universal in modern computing.

Digital circuit board illustrating two's complement binary representation
What Is Two's Complement? The Formula: -x = ~x + 1 Signed Range Sign Extension FAQ

What Is Two's Complement?

Two's complement is the standard method for representing signed integers in virtually every modern computer. It was adopted because it lets the same hardware adder circuit handle both signed and unsigned addition without any special logic. When I first learned this in my computer architecture class, the elegance of it struck me: subtraction becomes addition of a negative number, and the hardware does not need to care about sign.

In two's complement, the most significant bit (MSB) serves as the sign bit. If the MSB is 0, the number is positive or zero. If it is 1, the number is negative. But unlike older systems like sign-magnitude or one's complement, the weight of the MSB is negative: in an 8-bit two's complement number, the MSB represents -128 rather than +128.

I struggled with two's complement in college until I wrote a small program that showed the binary representation of negative numbers. Seeing that -1 is all 1s and -128 is 10000000 in 8-bit finally made it click for me.

# 8-bit two's complement examples
# Positive numbers: same as unsigned binary
 0 = 0000 0000
 1 = 0000 0001
 7 = 0000 0111
63 = 0011 1111
127 = 0111 1111   # largest positive

# Negative numbers: MSB is 1
-128 = 1000 0000   # most negative
 -1 = 1111 1111    # all bits set
 -5 = 1111 1011
-32 = 1110 0000

# Verify: (-5) + 5 = 0
  1111 1011  (-5)
+ 0000 0101  (+5)
= 0000 0000  (0, with carry-out discarded)

The property that makes two's complement work is that adding any number and its two's complement negative gives zero, with the carry out of the high bit being discarded. This is automatic in fixed-width arithmetic — the CPU does not need to check signs.

The Formula: -x = ~x + 1

The defining equation of two's complement is -x = ~x + 1. To negate any number, you flip all its bits (one's complement) and then add 1. This works for both positive and negative numbers — applying the transformation twice yields the original value.

# Negating 5 in 8-bit two's complement
x = 5          = 0000 0101
~x             = 1111 1010    (one's complement)
~x + 1         = 1111 1011    = -5 ✓

# Applying twice restores the original
y = -5         = 1111 1011
~y             = 0000 0100
~y + 1         = 0000 0101    = 5 ✓

# Negating 0 (the edge case)
x = 0          = 0000 0000
~x             = 1111 1111
~x + 1         = 0000 0000    (overflow discarded) = 0 ✓

# Negating -128 (the edge case — asymmetric!)
x = -128       = 1000 0000
~x             = 0111 1111
~x + 1         = 1000 0000    = -128 (!)
# This is the one case where negation returns itself due to overflow

The last example reveals the asymmetric range of two's complement: there is one more negative value than positive. In 8-bit, you can represent -128 but not +128. The negation of -128 overflows back to -128. This is not a bug in the system — it is a consequence of having an even number of bit patterns and reserving zero for the positive side.

Why NOT and Add 1?

The mathematical intuition: in n-bit unsigned arithmetic, ~x = (2^n - 1) - x (the one's complement). Adding 1 gives 2^n - x. Since 2^n wraps to 0 in n-bit arithmetic, -x and 2^n - x are congruent modulo 2^n. This is why the same adder circuit works for both signed and unsigned operations.

The Signed Range: -2^(n-1) to 2^(n-1)-1

The range of an n-bit two's complement number is asymmetric by design. There are 2^n distinct bit patterns. Zero occupies one pattern on the positive side, so the negatives get the remaining 2^(n-1) patterns while the positives get 2^(n-1)-1.

Bit WidthRangeZero PatternAll Ones
8-bit-128 to 1270000 0000-1 (255 unsigned)
16-bit-32,768 to 32,7670000 0000 0000 0000-1 (65,535 unsigned)
32-bit-2,147,483,648 to 2,147,483,6470x000000000xFFFFFFFF (-1)
64-bit-9,223,372,036,854,775,808 to 9,223,372,036,854,775,8070x00000000000000000xFFFFFFFFFFFFFFFF (-1)

This asymmetry is relevant in practice. The absolute value of the minimum negative number cannot be represented as a positive number in the same width. In C, -INT32_MIN is undefined behavior. In Java, Math.abs(Integer.MIN_VALUE) returns Integer.MIN_VALUE — a well-known quirk that I have seen cause subtle bugs in production code.

// The MIN_VALUE gotcha (Java example)
int min = Integer.MIN_VALUE;  // -2147483648
int abs = Math.abs(min);       // -2147483648 (!)
// There is no +2147483648 in 32-bit two's complement

// Safe absolute value using long
long safeAbs = Math.abs((long) min);  // 2147483648

// C example — undefined behavior
int x = INT32_MIN;
int y = -x;  // UNDEFINED BEHAVIOR: -(-2147483648) overflows 32-bit int

Sign Extension

When you widen a two's complement number to more bits (e.g., from 8-bit to 16-bit or from 32-bit to 64-bit), you must preserve the sign. This is done by sign extension: copying the sign bit into all the new higher-order bit positions. For positive numbers, this means filling with zeros. For negative numbers, it means filling with ones.

# Sign extension from 8-bit to 16-bit
# Positive: pad with zeros
  8-bit:  0001 1010  (+26)
 16-bit:  0000 0000 0001 1010  (+26) ✓

# Negative: pad with ones
  8-bit:  1111 0110  (-10)
 16-bit:  1111 1111 1111 0110  (-10) ✓

# What happens without sign extension (wrong):
  8-bit:  1111 0110  (-10)
 16-bit:  0000 0000 1111 0110  (+246) ✗

# In C, casting to a wider type does sign extension automatically
int8_t a = -10;          // 8-bit signed
int16_t b = a;           // 16-bit signed — sign extended automatically
uint16_t c = (uint16_t)a; // ALSO sign-extended! c = 0xFFF6, not 0x00F6

// To get zero extension, cast through unsigned first
uint16_t d = (uint16_t)(uint8_t)a;  // d = 0x00F6 (246)

Sign extension happens implicitly in most languages when you assign a smaller integer type to a larger one. The confusing part — and I have debugged this more than once — is that the implicit conversion also happens when casting a signed value to an unsigned wider type. In C, (uint16_t)(int8_t)(-10) is 0xFFF6, not 0x00F6. The sign bit propagates before the unsigned interpretation kicks in.

Zero Extension vs Sign Extension

When you need zero extension (treating the value as unsigned during widening), first cast to the unsigned version of the original width, then allow implicit widening. This is why (uint32_t)(uint8_t)(-1) gives 255, while (uint32_t)(int8_t)(-1) gives 4294967295. Our 32-bit programmer calculator lets you toggle between signed and unsigned interpretations to see the difference live.

See Two's Complement in Action

Enter any negative number in our bitwise calculator and watch the two's complement binary representation update in real time. Toggle between signed and unsigned views to understand how the same bit pattern can represent different values.

Frequently Asked Questions About Two's Complement Binary

What is two's complement?

Two's complement is the standard way computers represent signed integers. In this system, a negative number is created by inverting all bits of the positive number (one's complement) and then adding 1. The most significant bit acts as the sign bit: 0 for positive or zero, 1 for negative. This representation allows addition and subtraction to work identically on signed and unsigned values using the same hardware circuitry.

Why does -x = ~x + 1?

The formula -x = ~x + 1 is the mathematical definition of two's complement. Inverting all bits of x gives its one's complement, which is (2^n - 1) - x. Adding 1 gives 2^n - x, which is the two's complement representation of -x. For example, for x=5 in 8-bit: ~5 = 11111010, add 1 = 11111011 = -5. If you add x and its two's complement, you get 2^n, which overflows to 0 — proving it works.

What is the range of an n-bit two's complement number?

An n-bit two's complement number can represent values from -2^(n-1) to 2^(n-1)-1. For example: 8-bit range is -128 to 127, 16-bit range is -32768 to 32767, 32-bit range is -2147483648 to 2147483647, and 64-bit range is -9223372036854775808 to 9223372036854775807. There is always one more negative value than positive value because zero occupies one positive slot.

How does sign extension work in two's complement?

Sign extension preserves the value when widening a two's complement number to more bits. The sign bit (the most significant bit) is copied into all new higher-order bit positions. For positive numbers, this means filling with zeros. For negative numbers, it means filling with ones. For example, extending -3 from 4-bit (1101) to 8-bit gives 11111101, which is still -3.

Why does Math.abs(Integer.MIN_VALUE) return a negative number in Java?

In Java, Integer.MIN_VALUE is -2147483648 (0x80000000). Its two's complement negation would be +2147483648, but that value cannot be represented in a 32-bit signed integer because the maximum positive value is 2147483647. The negation overflows and returns the original value. This is a well-known asymmetry of two's complement: the most negative number has no positive counterpart in the same bit width.

Related Tools

Bitwise Calculator →

AND, OR, XOR, NOT, shifts

Bitwise Guide →

Complete operations reference

Hex to ASCII →

Decode hex strings

SHA256 Generator →

Compute hashes