fast-float
A zero-dependency Rust crate that parses decimal strings into f32/f64 up to 8x faster than the standard library, with full no_std support.
Repository Health
Technical Analysis
fast-float is a Rust port of Daniel Lemire’s fast_float C++ algorithm, providing a drop-in replacement for parsing decimal strings into IEEE-754 floating-point numbers. It exposes two entry points, parse() and parse_partial(), both generic over f32/f64 and accepting any AsRef<[u8]> input, so callers can parse owned strings, borrowed slices, or raw byte buffers without an intermediate allocation.
The crate’s core trick is a two-tier parsing strategy: a branch-light “fast path” handles the overwhelming majority of real-world numbers (up to 19 significant digits) using integer arithmetic and precomputed power-of-ten tables, while a Lemire-style Eisel-Lemire algorithm with 128-bit approximate multiplication covers the rest before falling back to an arbitrary-precision decimal parser for pathological inputs. The result is exact, round-to-even IEEE-754 parsing with no precision loss, benchmarked at up to 1.5 GB/s on modern hardware and 2-8x faster than Rust’s built-in FromStr implementation. It has no runtime dependencies, works in no_std environments, and its algorithm was influential enough to eventually be adopted into the Rust standard library’s own float parser.
What You Get
parse()andparse_partial()functions - full-string and longest-prefix parsing, both generic overf32/f64and any byte-slice-like input.- Exact IEEE-754 semantics - round-to-even conversion with no precision loss, matching what the C99/IEEE standard mandates.
- no_std compatibility - the
stdfeature can be disabled for embedded or kernel-level use with zero required dependencies. - Little- and big-endian support - correct on both, with extra SIMD-friendly optimizations enabled on little-endian architectures.
- Infinity/NaN and scientific notation parsing - handles
inf,nan, signed variants, and exponent notation out of the box. - Extensive correctness test suite - explicit edge-case tests, a ~5M-case file-based test corpus, exhaustive f32 roundtripping, and cargo-fuzz targets.
Common Use Cases
- CSV/TSV and log parsers - projects that need to convert millions of numeric fields per second without the overhead of
FromStr. - Serialization format implementations - JSON, CBOR, or custom binary/text formats that decode floating-point literals from raw bytes.
- Scientific and financial data ingestion pipelines - workloads dominated by numeric parsing where a 2-8x speedup meaningfully reduces wall-clock time.
- Embedded and no_std targets - firmware or kernel code that needs float parsing without pulling in the full standard library.
- Building higher-level parsers -
parse_partial()is designed as a primitive for hand-written recursive-descent or streaming parsers.
Under The Hood
Architecture
The crate is organized as a small pipeline of focused modules: parse.rs orchestrates the top-level flow, number.rs extracts a normalized Number { mantissa, exponent, negative, many_digits } struct from the input bytes using an AsciiStr cursor abstraction defined in common.rs, and the result is routed either through the integer-arithmetic fast path in number.rs (try_fast_path) or through the Eisel-Lemire approximation in binary.rs (compute_float, backed by a precomputed power-of-five table in table.rs) with a final fallback to the arbitrary-precision decimal parser in decimal.rs/simple.rs. A sealed Float trait in float.rs abstracts over f32/f64 bit layouts and per-type exponent/mantissa constants, so the entire pipeline is written once and monomorphized per float type. Nothing outside this three-stage funnel would need to change if a new float representation were ever added — the abstraction boundary is the Float trait itself.
Tech Stack
The library is pure Rust (edition 2018) with zero runtime dependencies and an optional std feature gating only the std::error::Error impl, making it usable in no_std contexts. Dev-dependencies (lexical-core, hexf-parse, ryu, fastrand, num-bigint) are scoped strictly to the test/benchmark suite and excluded from the published crate via exclude = ["benches/*", "extras/*"]. The repo is a Cargo workspace with two auxiliary members — extras/data-tests and extras/simple-bench — kept separate from the core crate so consumers never pull in benchmarking machinery, and CI runs the explicit test suites on every push via GitHub Actions.
Code Quality
Testing is unusually thorough for a crate this size: tests/test_basic.rs cross-checks results against both lexical_core and the standard library’s FromStr across hundreds of macro-generated cases, test_exhaustive.rs roundtrips all 4 billion possible f32 bit patterns via the ryu formatter, and extras/data-tests runs a ~5M-case file-based corpus inherited from the original C++ project, with a cargo-fuzz target for roundtrip fuzzing. The crate opts into clippy::all, clippy::pedantic, clippy::nursery, and clippy::cargo lint groups, and unsafe blocks (used for unaligned reads and unchecked table indexing in the hot path) are narrowly scoped and commented rather than sprinkled throughout.
What Makes It Unique Most languages’ float parsers either sacrifice precision for speed or sacrifice speed for exactness; fast-float does neither by implementing the Eisel-Lemire algorithm, which resolves the overwhelming majority of inputs exactly using only integer and 128-bit multiplication arithmetic, reserving the historically expensive arbitrary-precision path for genuinely rare inputs. The technique proved compelling enough that its author’s C++ original directly influenced float-parsing improvements adopted into the Rust standard library itself, and this crate remains a reference implementation for the algorithm in the Rust ecosystem.