The formula
N = d_(k−1)·b^(k−1) + … + d_2·b² + d_1·b¹ + d_0·b⁰
Every positional numeral is a polynomial in the base: each digit d_i (with 0 ≤ d_i < b) is multiplied by the base raised to its position, counting from zero at the right.
0.d_1 d_2 d_3 … = d_1·b⁻¹ + d_2·b⁻² + d_3·b⁻³ + …
Digits to the right of the point continue the same pattern with negative exponents. Nothing new happens at the point — the weights just keep dividing by b.
n bits ⇒ 2ⁿ states, unsigned range 0 to 2ⁿ − 1
A register of width n holds exactly 2ⁿ distinct patterns. A 12-bit ADC tops out at 0xFFF = 4095; a 16-bit word at 0xFFFF = 65535.
two's complement: N = −d_(n−1)·2^(n−1) + Σ d_i·2^i (i = 0 … n−2)
Signed integers reuse the same bits but give the top bit a negative weight. Same pattern, different contract: 0xFFFF is 65535 unsigned and −1 signed.
16 = 2⁴ and 8 = 2³
This is the whole reason hex and octal exist. One hex digit is exactly four bits; one octal digit is exactly three. Conversion between binary and hex is grouping, not arithmetic.
Where you meet it
- On the bench with a debugger. The datasheet says fault flags live in bits 2, 3, and 5 of the status register. Your JTAG session shows
0x2C. Expand it —0x2C = 0010 1100— and there they are, all three set. You do this forty times a day during bring-up, and the engineers who are slow at it are slow at bring-up. - On the test stand reading bus traffic. A 1553 or CAN capture is a wall of hex bytes. Whether frame word 3 reading
0x0FA0means 4000 counts of good data or garbage depends on you converting it in your head against the ICD while the test article is still hot. - In an ICD or memory-map review. Register offsets, base addresses, and packet layouts are written in hex because they describe hardware that is organized in powers of two. A reviewer who can't see that
0x1000is a 4096-byte boundary can't check the map for overlaps. - On the data server after the test.
chmod 755on the results directory is octal:0o755 = 111 101 101= owner rwx, group r-x, world r-x. Octal survives in Unix permissions because three bits per class maps to exactly one digit per class.
How it works
Converting out of a base is just evaluating the polynomial: 0x1F4 = 1·256 + 15·16 + 4 = 500. Converting into a base is the reverse — repeated division, keeping remainders. Take 3102 to hex: 3102 ÷ 16 = 193 r 14 (E), 193 ÷ 16 = 12 r 1, 12 ÷ 16 = 0 r 12 (C). Read the remainders last-to-first: 0xC1E. Check it: 12·256 + 1·16 + 14 = 3102.
You almost never do that at the bench, though, because hex-to-binary needs no arithmetic at all. Each hex digit is a four-bit group: 0xAB = 1010 1011 = 171. This is why engineers think in hex rather than decimal when staring at hardware — hex is binary with the boredom compressed 4:1. Octal does the same trick three bits at a time, which fit the 12- and 36-bit word machines it grew up on and fits almost nothing built since.
The gotchas are where the money is:
- A bit pattern has no meaning without its contract.
0xFFFFis 65535, or −1, or two bytes of 0xFF padding, depending on declared width and signedness. Sign extension is the classic trap: promote a signed 16-bit0x8000(−32768) into a 32-bit variable and you get0xFFFF8000, not0x00008000. Telemetry decoders get this wrong every year. - Endianness is not a base problem, but it ambushes you in the same memory dump. The bytes of a 32-bit value arrive in memory order, not significance order. Reading a little-endian dump as if it were big-endian produces confident, plausible, wrong numbers.
- Fractions don't survive base changes. A fraction terminates in base
bonly when its reduced denominator divides a power ofb. Decimal 0.1 has denominator 10 = 2·5, and the 5 has no home in base 2 — so 0.1 is infinitely repeating in binary, and an IEEE double stores it as 0.10000000000000000555…. This is why a test script that accumulates 0.1-second timesteps drifts, and why pass/fail comparisons on floats need a tolerance, not==. - The mistake people actually make is dropping the prefix. Written in an email, "register 10" is ambiguous three ways: 10, 0x10 = 16, or 0b10 = 2. In C source it's worse — a leading zero makes a literal octal, so
0755is 493 and someone's "cleaned up" alignment of0644to644just changed the value. Prefix every non-decimal constant, every time, including on the whiteboard.
The math itself has no limits of validity — any integer b ≥ 2 works, and the polynomial form is exact. The limits are all in the contracts layered on top: word width, signedness, byte order, and fixed-point scaling. Base conversion errors are contract errors wearing an arithmetic costume.
History
Positional notation is older than the alphabet. The Sumerians were counting in base 60 by the third millennium BC, and the Babylonians who inherited the system turned it into a true positional notation — two cuneiform symbols, position determining weight [1][2]. They ran it for centuries without a zero, leaving empty positions to context before a placeholder symbol finally appeared [1]. Their base is still on your wrist and your compass rose: 60 seconds, 60 minutes, 360 degrees [2].
Binary as a worked-out arithmetic belongs to Gottfried Leibniz, who had the system complete by 1679 and published it in the Paris Academy's memoirs as Explication de l'Arithmétique Binaire in 1703 — including, in the paper itself, a discussion of the ancient Chinese hexagram figures he saw prefigured in it [3][4]. Binary stayed a curiosity for two centuries until Claude Shannon's 1937 MIT master's thesis, published in 1938, showed that relay switching circuits and Boolean algebra were the same subject — the founding document of digital logic design [3][5].
The base you read at the console then followed the hardware. Machines with word sizes divisible by three — like DEC's 12-bit PDP-8, launched in 1965 — were documented and toggled in octal, three bits per digit [6][7]. IBM's System/360, announced in April 1964, standardized the 8-bit byte and byte-addressable memory [9][10], and four bits per digit called for base 16. Early machines had improvised hex digits — ORDVAC used K, S, N, J, F, L in 1952 — but the now-universal A through F became the de facto standard after 1966, following the Fortran IV manual for System/360 [8]. Octal retreated to Unix file permissions [6], and hex has owned the console ever since.
Related tools
- /tools/adc-resolution — LSB size and counts for n-bit converters; the register values you'll be converting live here
- /tools/snr-enob — effective number of bits from measured SNR; the analog side of the same bit budget
- /tools/resistor-color-code — another positional digit encoding engineers read on sight, base 10 with a power-of-ten multiplier band
Sources
- https://mathshistory.st-andrews.ac.uk/HistTopics/Babylonian_numerals/
- https://en.wikipedia.org/wiki/Sexagesimal
- https://en.wikipedia.org/wiki/Binary_number
- https://mathshistory.st-andrews.ac.uk/Biographies/Leibniz/
- https://en.wikipedia.org/wiki/A_Symbolic_Analysis_of_Relay_and_Switching_Circuits
- https://en.wikipedia.org/wiki/Octal
- https://en.wikipedia.org/wiki/PDP-8
- https://en.wikipedia.org/wiki/Hexadecimal
- https://en.wikipedia.org/wiki/IBM_System/360
- https://en.wikipedia.org/wiki/8-bit_computing