Gray Code Explained: Why Rotary Encoders Use It
TL;DR: Gray code is a binary numeral system where successive values differ by exactly one bit. Rotary encoders, FIFO pointers, and Karnaugh maps use it because single-bit transitions eliminate the transient invalid states that plague straight binary counters during simultaneous bit flips.
A standard 4-bit binary counter advancing from to flips all four bits at once. In an idealized timing diagram those bits change simultaneously. In real silicon — with mismatched gate delays, slewing edges, and noisy supply rails — they change in some unspecified order, producing a brief flicker of intermediate values like or on the bus. For a CPU register that’s harmless: the value is sampled by a clock edge after everything settles. For a sensor read by an asynchronous observer, those glitches are real readings.
Gray code, also called reflected binary code, eliminates the problem at its root: any two adjacent values in the sequence differ in exactly one bit. There is no simultaneous flip to glitch through.
Why Does Single-Bit Adjacency Matter?
Consider a rotary encoder — the optical or magnetic disc that tells a motor controller what angle a shaft is at. The disc has concentric tracks, each track contributing one bit of the position word, read by a sensor at a fixed angle. As the disc rotates, the sensor sees a sequence of binary values.
If the tracks are encoded in straight binary, the boundary between position () and position () requires three tracks to change state at the same physical angle. Manufacturing tolerances guarantee the sensors don’t actually see all three transitions at the same instant. For a few microradians the controller might read (position 7), (position 0), or any other intermediate. A motor servo loop reading garbage at the boundary will twitch, oscillate, or — in safety-critical applications — fail.
Encoding the same disc in Gray code makes every angular boundary a single-bit transition:
| Position | Binary | Gray |
|---|---|---|
| 0 | 000 | 000 |
| 1 | 001 | 001 |
| 2 | 010 | 011 |
| 3 | 011 | 010 |
| 4 | 100 | 110 |
| 5 | 101 | 111 |
| 6 | 110 | 101 |
| 7 | 111 | 100 |
Look at the Gray column. Between any two adjacent rows exactly one bit flips. Between row 7 and row 0 (wrapping around), exactly one bit flips. The sequence is cyclic and unit-distance. There is no boundary where mismatched sensor timing can produce a wrong reading — at worst the sensor reports the old value or the new value, never something else.
The 4-Bit Gray Code Table
The pattern extends naturally:
| Decimal | Binary | Gray |
|---|---|---|
| 0 | 0000 | 0000 |
| 1 | 0001 | 0001 |
| 2 | 0010 | 0011 |
| 3 | 0011 | 0010 |
| 4 | 0100 | 0110 |
| 5 | 0101 | 0111 |
| 6 | 0110 | 0101 |
| 7 | 0111 | 0100 |
| 8 | 1000 | 1100 |
| 9 | 1001 | 1101 |
| 10 | 1010 | 1111 |
| 11 | 1011 | 1110 |
| 12 | 1100 | 1010 |
| 13 | 1101 | 1011 |
| 14 | 1110 | 1001 |
| 15 | 1111 | 1000 |
The “reflected” name comes from the construction rule: to build the -bit code, take the -bit code, mirror it vertically, prepend a to the original half and a to the mirrored half. Mirroring guarantees the boundary between halves is a single-bit transition (the new MSB).
Binary-to-Gray Conversion
The conversion from binary to Gray is one of the cleanest formulas in digital logic:
with for the MSB. As a single expression:
In hardware that’s literally a chain of XOR gates — one per bit, with no carry chain. The conversion is purely combinational and finishes in a single gate delay regardless of word width. To see why XOR is the right operator, revisit the XOR gate’s role as a difference detector: it returns 1 exactly when adjacent bits disagree, and disagreement between adjacent binary positions is precisely what marks a Gray-code transition.
Worked example. Convert binary to Gray.
Result: Gray , which the table above confirms is the encoding of decimal 11.
Gray-to-Binary Conversion
The inverse direction is slightly less elegant — it has a dependency chain:
Equivalently, walking from the MSB down: , then . Each binary bit is the running XOR of all Gray bits at or above its position. This does form a propagation chain — the gray-to-binary converter for an -bit word has XOR delays in series, the same critical-path shape as a ripple-carry adder.
For the example above, decoding Gray :
Recovered binary: , decimal 11. Round-trip clean.
Beyond Encoders: Clock Domain Crossing
Rotary encoders are the textbook motivation, but the most consequential modern use of Gray code lives inside FPGAs and SoCs: asynchronous FIFO pointers for clock-domain crossing (CDC).
A FIFO that bridges two clock domains needs the read-side logic to know how much data the write-side has produced, and vice versa. That means each side must sample the other side’s pointer. Sampling a multi-bit binary counter that just incremented across a multi-bit boundary risks reading a value that never existed — exactly the rotary-encoder problem in a different costume.
Encoding the pointers in Gray code guarantees that no matter when the receiving flip-flops sample, they see either the old pointer or the new pointer, never garbage. Combined with a two-flop synchronizer to handle setup, hold, and metastability, Gray-coded pointers are the canonical CDC technique. Every commercial async FIFO IP block uses them.
A Gray-Code Counter in Hardware
You can’t simply replace a binary counter’s flip-flops to get a Gray counter — the next-state logic has to encode the unit-distance transition rule directly. Two practical approaches:
- Binary counter + binary-to-Gray converter. Run a normal binary COUNTER and feed its output through the XOR network. The COUNTER itself can glitch internally, but the Gray output transitions cleanly because the XOR mask absorbs the multi-bit flips. This is the dominant implementation when only the output needs to be glitch-free.
- Native Gray counter. A state-machine flip-flop bank with explicit next-state logic for each Gray transition. More complex to derive but avoids the binary intermediate. Useful when the counter itself needs to be sampled mid-flight.
To experiment with the first approach, open the 4-bit binary counter template, tap the four output bits, and add three XOR gates implementing . Probe both buses on the simulator’s logic analyzer — the binary bus shows multi-bit transitions per clock; the Gray bus shows exactly one bit changing per tick.
Drawbacks: The Math Is Awkward
Gray code’s superpower — single-bit transitions — comes with a price: arithmetic in Gray code is painful. There is no carry-propagation formula that works directly on Gray-coded values. Adding two Gray numbers requires converting both to binary, adding, and converting back. Multiplication and shifting are similarly broken.
That’s why no general-purpose ALU operates in Gray code. The encoding is a transport format: clean transitions on the wire, decoded back to binary at the destination for any actual computation. Karnaugh maps inherit the same property — adjacent cells differ by one bit specifically so that grouping minterms into prime implicants works geometrically — but the simplification yields a Boolean expression in standard form, not a Gray-coded one.
Historical Note
The encoding is named after Frank Gray, a Bell Labs engineer who patented its use in pulse-code modulation in 1953. The underlying mathematics — a Hamiltonian path through the corners of an -dimensional hypercube where each step traverses one edge — appeared in earlier work by figures like Émile Baudot (1870s telegraph codes) and Louis Gros (1872, Théorie du Baguenodier, on the Chinese rings puzzle), as well as in much older studies of binary representations across cultures. Gray’s contribution was the rigorous application to error-resistant signaling; the name stuck.
Common Pitfalls
- Assuming arithmetic works. Adding Gray-coded values bit-wise produces nonsense. Always convert to binary first.
- Ignoring the MSB-zero convention. The binary-to-Gray formula assumes a virtual zero above the MSB. Forgetting this flips the sign of the high bit.
- Mixing Gray with shift-register loops. A standard shift register, like the SISO/SIPO designs, shifts bits laterally and doesn’t preserve unit-distance properties end-to-end. Convert at the boundaries.
- Confusing Gray with one-hot. One-hot encoding (only one bit ever set) is also glitch-resistant in a different sense, but it’s wide instead of . Gray code is the compact choice.
What’s Next
The next post in this series, SISO, SIPO, PISO, PIPO: Shift-Register Modes Compared, walks through the four shift-register configurations that move bits between serial and parallel forms — including how Gray-coded synchronizers fit into the larger CDC picture.
To see Gray code in action right now, load the 4-bit binary counter template, wire its outputs through three XOR gates as described above, and watch the difference on a logic analyzer. The single-bit-transition property isn’t a textbook abstraction — it’s visible on the trace within the first few clock cycles.