Binary

Binary uses only two symbols: 0 and 1, which is why it may also be called base 2. It’s the number system computers use to store data. A transistor may contain electrons (1) or it may not (0).

Here’s how binary numbers count, compared to decimal:

Binary (base 2)Decimal (base 10)
00
11 (2^0)
102 (2^1)
113
1004 (2^2)
1015
1106
1117
10008 (2^3)
10019
101010
101111
110012
110113
111014
111115
1000016 (2^4)

A bit is a single binary digit, either 0 or 1. A byte is 8 bits. For example, 11111111 (one byte of all 1s) equals 255 in decimal. 8 bytes = 64 bits.

Bit-width in computers

When we say a computer is 32-bit or 64-bit, we mean that its CPU registers are 32 or 64 bits wide. The width of a register determines the largest number it can store directly.

  • A 32-bit register can store numbers from 0 to 11111111 11111111 11111111 11111111 (decimal 4,294,967,295, or 2^32 - 1).
  • A 64-bit register can store numbers from 0 to 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 (decimal 18,446,744,073,709,551,615, or 2^64 - 1).

Signed vs unsigned integers

Unsigned integers can only represent non-negative numbers, ranging from:

0 to (2^n - 1) where n is the number of bits.

Signed integers can represent both positive and negative numbers, using a system called two’s complement. In two’s complement, the first bit is the sign bit:

  • 0 means positive
  • 1 means negative

This leaves n - 1 bits for the magnitude.

Examples for signed 32-bit integers:

01111111 11111111 11111111 11111111 =  2147483647
00000000 00000000 00000000 00000001 =  1 
00000000 00000000 00000000 00000000 =  0 
10000000 00000000 00000000 00000000 = -2147483648
10000000 00000000 00000000 00000001 = -2147483647
11111111 11111111 11111111 11111111 = -1

Examples for unsigned 32-bit integers:

00000000 00000000 00000000 00000001 = 1 
00000000 00000000 00000000 00000000 = 0 
01111111 11111111 11111111 11111111 = 2147483647
10000000 00000000 00000000 00000000 = 2147483648
10000000 00000000 00000000 00000001 = 2147483649
11111111 11111111 11111111 11111111 = 4294967295

Floating point numbers

Integers are exact in binary, but fractional numbers are not. A float is stored in scientific notation: a sign bit, an exponent, and a mantissa, roughly ±mantissa × 2^exponent:

Mantissa (noun) man·​tis·​sa

The part of a logarithm to the right of the decimal point

TypeBitsSignExponentMantissa
Single (float)321823
Double (double)6411152

Just like 1/3 cannot be written exactly in decimal, most decimal fractions cannot be written exactly in binary. Binary can only represent sums of powers of two, so 0.1 becomes a repeating binary fraction:

0.1 = 0.0001100110011001100110011...

The value is rounded to the nearest representable mantissa, and the rounding errors accumulate across operations:

0.1 + 0.2 = 0.30000000000000004

not 0.3. This is why comparing floats with == is unreliable, and why calculators have to round their results before displaying them: showing the raw value would print 0.30000000000000004 instead of 0.3. Getting that right is one of the classic challenges of implementing a calculator.

Examples

Single precision (float), 32 bits, parsed as (-1)^sign × 1.mantissa × 2^(exponent - 127):

00111111 10000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^0   = 1.0
00111111 00000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^-1  = 0.5
00111110 10000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^-2  = 0.25
00111101 10000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^-3  = 0.125
00111101 00000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^-4  = 0.0625
00111110 11001100 11001100 11001101 = (-1)^0 × 1.4CCCCD × 2^-2  = 0.4
00111110 01001100 11001100 11001101 = (-1)^0 × 1.4CCCCD × 2^-3  = 0.2
00111110 10011001 10011001 10011010 = (-1)^0 × 1.19999A × 2^-2  = 0.3
00111111 00011001 10011001 10011010 = (-1)^0 × 1.19999A × 2^-1  = 0.6
00000000 00000000 00000000 00000000 = 0                            (special case)
00110101 10000110 00110111 10111101 = (-1)^0 × 1.0637BD × 2^-20  = 0.000001
10111111 10000000 00000000 00000000 = (-1)^1 × 1.000000 × 2^0   = -1.0
01001111 00000000 00000000 00000000 = (-1)^0 × 1.000000 × 2^31  = 2147483648

The powers of two (1, 0.5, 0.25, 0.125, 0.0625) are exact: their mantissas are all zeros and only the exponent changes. The rest are rounded, and the repeating 1100/1001 mantissa patterns are the binary tails of fractions like 1/3 and 1/10. Note that 2147483647 as a float rounds up to 2147483648 (2^31).

Double precision (double), 64 bits, parsed as (-1)^sign × 1.mantissa × 2^(exponent - 1023):

00111111 10111001 10011001 10011001 10011001 10011001 10011001 10011010 = (-1)^0 × 1.999999999999A × 2^-4 = 0.1
00111111 11001001 10011001 10011001 10011001 10011001 10011001 10011010 = (-1)^0 × 1.999999999999A × 2^-3 = 0.2
00111111 11010011 00110011 00110011 00110011 00110011 00110011 00110011 = (-1)^0 × 1.3333333333333 × 2^-2 = 0.3
00111111 11010011 00110011 00110011 00110011 00110011 00110011 00110100 = (-1)^0 × 1.3333333333334 × 2^-2 = 0.30000000000000004 (0.1 + 0.2)

0.3 and 0.1 + 0.2 differ by exactly one bit in the last position: that single bit is the whole 0.00000000000000004.