Herbie: Rewriting Floating-Point Expressions Before They Lose Precision
Optimize floating-point expressions for accuracy
At a glance
- What is it?
- Herbie takes an FPCore expression and searches for an algebraically different form that evaluates more accurately in floating point. It is a Racket package with a Rust component, a web interface, and a shell, and it targets people whose numerical code is already written and already wrong at the last few bits.
- Who is it for?
- Adopt Herbie if you have a small number of scalar expressions whose accuracy you can state as an FPCore input and whose rewritten form you are willing to read and re-verify. Do not adopt it if you need a drop-in pass over a whole C or Fortran translation unit, or if your error comes from accumulated state rather than a single expression.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly HTML, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The failure Herbie is built to catch
Floating-point error is not evenly distributed. Most expressions are fine. A few lose most of their significant digits to cancellation, and the loss is invisible in the source because the algebra looks correct. The README's own example is the smallest version of this: (FPCore (x) (- (+ 1 x) x)). In exact arithmetic that is 1. In floating point, for large x, the addition of 1 is absorbed and the subtraction returns zero. Herbie answers with 1, which is the value the algebra promised and the hardware does not deliver. That gap is the whole subject of the tool. The audience is narrow on purpose: people writing numerical kernels, library functions, or simulation inner loops who can express the computation as a single expression in FPCore and who care about the last bits rather than the first ones. If your bug is an off-by-one in an index, Herbie has nothing to say.
Search, not simplification: what happens between input and output
Herbie is not a symbolic simplifier. A simplifier rewrites toward fewer operations; Herbie rewrites toward lower error, and those two goals frequently disagree. The mechanism implied by the repository layout and the README is a generate-and-test loop: the input expression is sampled over its input domain, each candidate rewrite is evaluated on those samples, and the candidate whose error against a high-precision reference is lowest wins. The output is another FPCore expression, not a proof and not a certificate. That distinction matters when you read the result. The rewritten form is a claim about the sampled points, and the documentation's framing of Herbie as improving error rather than guaranteeing it is consistent with that. The search space is algebraic identities, so the tool can return a form that is mathematically equal to your input but numerically unrelated, and it can also return a form that is worse on a region the sampler did not visit. The repository is primarily HTML by language count, which reflects the web interface and documentation rather than the core; the core is Racket with a Rust component, and the source install requires Racket 8.0 or later and Rust 1.87.0 or later.
Installing from the Racket package archive
The README recommends the package archive over a source checkout. Install Racket, then run raco pkg install --auto herbie. That single command pulls the package and its dependencies. The README states that Windows, Linux, and macOS are supported on both x86 and AArch64. One platform note is given explicitly and is worth repeating because it costs an afternoon to diagnose: on Linux, avoid the Snap installer for Racket. After installation, racket -l herbie starts the tool. If you want the web interface instead, the command is racket -l herbie web. The source route exists for people working on Herbie itself and uses make install from the repository root, with the same Racket and Rust version floors.
The shell, the batch commands, and the FPCore contract
The interactive path is racket -l herbie shell. The README shows a session banner reading Herbie 1.3 with a seed, a prompt, and the (FPCore (x) ...) input echoed back with the rewritten result. Note the version string in that transcript does not match the current release line, which is at v2.3 as of July 2026; treat the transcript as illustrative of the interface, not of current output. Input is FPCore 1.2, a small S-expression format, and the README points at bench/ in the repository for more examples. Beyond the shell there are two batch commands, improve and report, documented on the options page rather than in the README. That split is a real friction point: the README gets you to a prompt, and everything about non-interactive use, including how to feed a file of expressions and where results land, lives in the online documentation. Budget time to read it before wiring Herbie into anything automated.
Where the approach stops working
Herbie operates on one expression at a time. Error that accumulates across a loop, a reduction over a large array, or a sequence of dependent updates is outside what a single FPCore rewrite can express, and no amount of search will recover it. There is also a verification burden that the tool does not remove. The output is an expression you must read, and for anything non-trivial you need your own high-precision check on the inputs you actually care about, because the search is driven by sampling and your domain may not be sampled the way you assume. The seed in the shell banner is a hint that the process is stochastic; the README's transcript prints one, which means results can vary between runs. If you need a deterministic, reproducible rewrite for a build pipeline, that variability is a design property to plan around, not a bug to file. Finally, Herbie rewrites expressions, not programs. It will not touch your memory layout, your compiler flags, or your choice of algorithm, and those often dominate the error budget.
How this differs from a compensated-arithmetic library
The obvious alternative approach is to keep the expression and change the arithmetic: use a compensated summation routine, a double-double type, or a fused multiply-add where available. That route is deterministic, it composes into loops and reductions, and it costs performance in a way you can measure. Herbie makes the opposite trade. It leaves the arithmetic alone and changes the algebra, which means the rewritten expression runs at ordinary speed on ordinary hardware, but the transformation is found by search and applies to one expression rather than a program. Neither is a superset of the other. If your error is in a dot product over a million elements, compensated summation is the right tool and Herbie is not. If your error is in a closed-form formula with catastrophic cancellation in one subterm, Herbie can find a rearrangement that a compensated library would never suggest, because the library does not rewrite your formula.
Maintenance, licensing, and what the repository does not tell you
The release cadence visible in the material is roughly annual: v2.1 in July 2024, v2.2 in August 2025, v2.3 in July 2026. That is a slow-moving project, which cuts both ways. You are unlikely to be chasing breaking changes every quarter, and you are also unlikely to get a quick fix for a platform quirk. The source install pins two toolchains, Racket 8.0 or later and Rust 1.87.0 or later, so a source build inherits the upgrade obligations of both. The package-archive install hides most of that but still requires a Racket runtime on every machine that runs Herbie. The licence field for this repository is reported as NOASSERTION, meaning no standard licence identifier was detected. That is not a statement about the actual terms; it means you cannot infer them from the metadata. Read the licence file in the repository and confirm the terms with whoever handles licensing at your organisation before shipping anything derived from Herbie's output, and note that the question of whether a machine-generated rewrite carries a copyright interest is separate from the question of the tool's own licence.
Editorial conclusion
Adopt Herbie if you have a small number of scalar expressions whose accuracy you can state as an FPCore input and whose rewritten form you are willing to read and re-verify. Do not adopt it if you need a drop-in pass over a whole C or Fortran translation unit, or if your error comes from accumulated state rather than a single expression. Before trusting any output, run the same expression through the shell at two or three different seeds and confirm the rewritten form is stable, then check the reported error against a higher-precision evaluation of your own.
Community notes