Unix DC
The reverse-Polish, arbitrary-precision desk calculator that was the first program ever run on Unix's PDP-11 — and still ships with virtually every Unix-like system today.
Created by Robert Morris and Lorinda Cherry
Unix dc (for “desk calculator”) is a reverse-Polish, arbitrary-precision calculator language and one of the oldest pieces of software still in everyday use. Written at Bell Labs by Robert Morris and Lorinda Cherry, dc predates the C programming language, predates Unix on the PDP-11 — it was, by Ken Thompson’s account, the first program run on that machine — and appears in the very first edition of the Unix Programmer’s Manual, dated November 3, 1971. More than fifty years later, an implementation of dc ships with virtually every Unix-like operating system, its terse single-character commands and unbounded precision essentially unchanged.
dc is often described as the oldest surviving Unix language: beneath its calculator exterior is a genuine programming language, with registers, strings, executable macros, and conditionals that together make it Turing-complete.
History & Origins
Before Unix Had an Assembler
dc’s origin story is inseparable from the origin of Unix itself. When Bell Labs acquired a PDP-11 around 1970 — the machine on which Ken Thompson and Dennis Ritchie would build the Unix that conquered the world — the system arrived before its own software ecosystem existed. dc, written in B, the typeless predecessor of C, was the first program to run on the new computer, before even an assembler was working. Thompson has opined that dc was the very first program written on the machine at all. A humble desk calculator was, in effect, the shakedown cruise for the hardware that Unix and C grew up on.
The First Edition Manual
The First Edition Unix Programmer’s Manual (November 3, 1971) documents dc (I) as “an arbitrary precision integer arithmetic package.” The 1971 dc was already recognizably the language used today: numbers pushed on a stack, + - * / % operating on the top two entries, s and l to store and load named registers, d to duplicate the top of stack, and p to print it. Input beginning with a zero was taken as octal. The manual’s worked example computes the monthly, weekly, and hourly rates for a $10,000-a-year salary — in pennies, since this version handled only integers — and its BUGS section admits with early-Unix candor that the remainder operator “doesn’t work correctly.”
The attribution of dc is a small piece of Unix lore in itself: the program is credited to Robert Morris and Lorinda Cherry, whose names appear on the definitive dc paper, while the First Edition manual page lists “ken” — Ken Thompson — as its owner, a reminder of how small and intertwined the early Unix group was. Morris went on to become chief scientist at the NSA’s National Computer Security Center; Cherry became one of the most influential members of the Unix group, later co-creating bc and the Writer’s Workbench text-analysis tools.
Maturity by Seventh Edition
By Seventh Edition Unix (1979), whose manual includes Morris and Cherry’s paper DC – An Interactive Desk Calculator, the language had grown into its final shape: arbitrary-precision decimal fractions with a user-controlled scale, strings enclosed in square brackets, the x command to execute a string as a macro, comparison commands that conditionally execute registers, and array storage. Those additions turned a calculator into a programming language — recursion via macros that invoke themselves gives dc full Turing-completeness.
Design Philosophy
Reverse Polish, No Ceremony
dc is a pure stack machine exposed directly to the user. There is no operator precedence to remember because there are no infix expressions: operands go on the stack, operators pop them and push results. Nearly every command is a single character. This makes dc programs famously terse — and famously cryptic — but the design is entirely consistent: learn perhaps two dozen characters and you have learned the whole language. It is worth noting that dc’s reverse-Polish interface predates the HP-35, the handheld calculator that popularized RPN, which was introduced in 1972.
Arbitrary Precision as a Foundation
From the beginning, dc’s defining capability was arithmetic on numbers of unlimited size. In 1971, when machine words were 16 bits, dc would happily multiply hundred-digit integers. That capability is why dc kept finding new work for decades: it was the readily available bignum engine on every Unix system, whether the caller was the bc compiler in 1975 or a three-line RSA implementation in 1995.
A Calculator That Is Secretly a Language
dc’s programmability is built from a few orthogonal pieces rather than dedicated syntax: strings are values, registers hold values, and x executes a string. A conditional is just “compare and maybe execute this register”; a loop is a macro that re-executes itself. There are no keywords anywhere in the language.
Key Features
- Arbitrary-precision arithmetic — integers and, since the 1970s, decimal fractions of unlimited size, with the scale (digits after the decimal point) under program control via
k - Pure stack model — all computation happens on a single value stack manipulated by one-character commands (
dduplicate,rswap in modern implementations,fdump the stack) - Named registers — 256 single-character registers, each of which is itself a stack in modern implementations
- Strings and macros —
[...]pushes a string;xexecutes one as dc code, and macros calling themselves provide recursion and loops - Conditional execution — comparison commands (
<x,>x,=xand negated forms) pop two values and execute a register on a match, providing branching - Arrays — indexed storage and retrieval per register via
:and; - Input/output radix control —
iandoset input and output bases independently, making dc a handy base converter - Turing-completeness — the macro-and-conditional combination is sufficient for general computation, unusual for something billed as a calculator
Code Examples
The classic first taste — push two numbers, add, print:
| |
Set the scale to 20 digits and take a square root:
| |
Factorial via a recursive macro — the idiomatic demonstration of dc as a real language. The macro is stored in register F and calls itself while the top of stack exceeds 1:
| |
Reading it character by character: [d1-d1<F*] pushes a macro that duplicates the current value, subtracts 1, and — if the result is still greater than 1 — recurses before multiplying; dsFx duplicates the macro string, stores one copy in register F, and executes the other; p prints the result, 10!.
Relationship with bc
No account of dc is complete without bc, its lifelong companion. In 1975, Lorinda Cherry wrote bc for Version 6 Unix as a front end to dc: bc parsed familiar infix expressions, variables, and C-like function definitions, compiled them into dc’s postfix commands, and piped the result to dc, which performed every actual calculation. For decades, users who thought they were using bc were running dc underneath.
The relationship later inverted. POSIX standardized bc in 1991 but never standardized dc, and the GNU implementations decoupled the pair: Philip Nelson’s GNU bc (first released in 1991) is a self-contained bytecode interpreter, while Ken Pizzini’s GNU dc — distributed in the same package — is built on bc’s arithmetic library. The child now carries the parent.
Evolution
dc’s implementation history spans the whole arc of Unix. The original B-language program was succeeded by the C implementation documented in the Seventh Edition manual, which flowed into the BSDs and commercial Unixes; Plan 9 carried its own dc forward from the research lineage. In the free-software era, GNU dc became the version most Linux users encounter, and it remains actively maintained — GNU bc 1.08 with dc 1.5.0, released in early January 2025 (with point releases through May 2025), added readline and libedit line-editing support and a sandboxing option that disables dc’s ! shell-escape command.
The most significant modern development is Gavin D. Howard’s independent bc/dc implementation, written in ISO C99 and permissively licensed. FreeBSD 13.0 (released in 2021) replaced its bc and dc utilities with Howard’s versions — its release notes cite GNU bc extensions, fixes for POSIX-compliance issues, and describe the new utilities as much faster than the programs they replaced; the same codebase ships in Android and, as of macOS 13, in macOS. It says something about dc’s standing that in the 2020s, multiple actively developed, competing implementations of a 1970 calculator exist.
dc and the Crypto Wars
dc earned an unlikely place in political history in 1995, when cryptographer Adam Back — later known for Hashcash and cited in the Bitcoin whitepaper — compressed a working RSA implementation into a three-line Perl email signature. The Perl merely marshaled data; the arbitrary-precision modular exponentiation at RSA’s core was done by piping a program to dc. Because US export regulations then classified strong cryptography as munitions, the signature (and the T-shirts it was printed on) became a famous act of civil disobedience, demonstrating that a “restricted weapon” fit in a few hundred characters of shell-callable calculator code. Related tricks, such as Diffie–Hellman key exchange written in dc, circulated among cypherpunks during the same export-control debates. The US export rules were substantially relaxed in 1999–2000.
Current Status
dc is alive in the unglamorous way that only bedrock Unix tools are: installed by default or one package away on GNU/Linux, the BSDs, and macOS, exercised daily in shell scripts, and maintained in at least two active implementations (GNU dc and Gavin Howard’s). It was never standardized by POSIX — bc absorbed that role — so portability rests on convention, and small differences exist between implementations (the r swap command and stacked registers, for example, are extensions relative to the oldest versions). There is no official Docker image; none is needed, since dc is present in or trivially added to essentially any Linux container base image via the distribution’s package manager (apt-get install dc on Debian/Ubuntu, for example, or the bc package family elsewhere).
Why dc Matters
dc’s claim on history is threefold. It is a first: the first program on the PDP-11 that Unix grew up on, older than C, present in Unix’s first manual — the oldest surviving Unix language. It is a survivor: fifty-plus years of continuous availability with its core design intact, a longevity almost no software achieves. And it is a miniature masterclass in language design: from a stack, single-character commands, strings, and one execute primitive, it derives conditionals, loops, recursion, and Turing-completeness with no syntax to speak of. Every developer who has piped an impossible-precision calculation through dc -e, and every student who has puzzled out [d1-d1<F*]dsFxp, is touching the same small, sharp tool that Bell Labs used to warm up the machine on which modern computing was built.
Timeline
Notable Uses & Legacy
PDP-11 bring-up at Bell Labs
Before Unix itself was running on Bell Labs' newly arrived PDP-11 around 1970, dc — cross-compiled from the B language — was used to exercise the machine, making a desk calculator the first program in the lineage that produced Unix and C.
The bc calculator
Traditional Unix bc was not a calculator at all but a compiler: Lorinda Cherry's 1975 front end translated familiar infix expressions and C-like functions into dc programs and piped them to dc, which did all the real arithmetic for a generation of Unix systems.
Adam Back's RSA munitions signature
The famous 1995 "RSA in 3 lines of Perl" email signature — a protest against US export controls that classified strong cryptography as munitions — used dc as its arbitrary-precision engine, piping a dc program that performed the modular exponentiation at the heart of RSA.
Shell scripting on Unix-like systems
Because dc ships in the base system or standard packages of GNU/Linux distributions, the BSDs, and macOS, it remains a lightweight way for shell scripts to do arbitrary-precision arithmetic — computing with hundreds of digits where shell integer arithmetic or floating point would overflow or lose precision.