A double-to-string conversion algorithm based on Schubfach and xjb with implementations in C and C++
- Round trip guarantee
- Shortest decimal representation
- Correct rounding
- High performance
- Small binary size
- Fast compile time
- IEEE 754
doubleandfloatsupport - Safer API than classic
dtoa - User-friendly output format similar to Python's default representation
- Negative zero dependencies
- Small, clean codebase consisting of one source file and an optional header
- Permissive license
#include "zmij.h"
#include <stdio.h>
int main() {
char buf[zmij::double_buffer_size + 1];
auto end = zmij::write(buf, sizeof(buf), 5.0507837461e-27);
*end = '\0';
puts(buf);
}On an Apple M5 Max running macOS, compiled with Clang 21.0, Żmij is more than
7x faster than Ryū, used by multiple C++
standard library implementations, ~13x faster than
double-conversion and ~100x
faster than sprintf on
dtoa-benchmark.
Conversion time (smaller is better):
ostringstream and sprintf are left out of the charts to keep the faster
methods readable.
On an AMD EPYC 7C13 (Milan) running Linux, compiled with GCC 13.3, Żmij is
~3.8x faster than Ryū, ~7x faster than double-conversion and ~34x faster than
sprintf.
Conversion time (smaller is better):
ostringstream and sprintf are left out of the charts to keep the faster
methods readable.
Compile time is ~135ms by default and ~180ms with optimizations enabled as measured by
% time c++ -c zmij.cc [-O2]
taking the best of 3 runs.
- C++: https://github.com/vitaut/zmij/blob/main/zmij.cc (reference implementation)
- C: https://github.com/vitaut/zmij/blob/main/zmij.c
- Rust: https://github.com/dtolnay/zmij
- Zig: https://github.com/de-sh/zmij
- 1 instead of 3 multiplications by a power of 10 in the common case
- Faster logarithm approximations
- Faster division and modulo
- Fewer conditional branches
- More efficient significand and exponent output
- Improved storage of powers of 10
- SIMD support
Żmij (pronounced roughly zhmeey or more precisely /ʐmij/) is a Polish word that refers to a mythical dragon- or serpent-like creature, continuing the dragon theme started by Steele and White.
A nice bonus is that the name even contains a "floating point" in its first letter. And to quote Aras Pranckevičius, "Żmij is also literally a beast."
We would like to express our gratitude to the individuals who have made Żmij possible:
-
Victor Zverovich (@vitaut) - Original author and maintainer of Żmij.
-
Tobias Schlüter (@TobiSchluter) - Contributed significant performance and portability improvements, including SIMD/SSE support and core algorithm refinements that enhance execution speed and cross-platform compatibility.
-
Dougall Johnson (@dougallj) – Authored the NEON implementation and contributed many optimization ideas, substantially improving performance on ARM platforms.
-
Alex Guteniev (@AlexGuteniev) - Contributed multiple fixes and improvements across build systems, platform compatibility, and testing infrastructure.
-
Xiang JunBo (@xjb714) - Contributed high-performance BCD digit extraction algorithm and additional optimization ideas used across scalar and SIMD code paths. The double path uses xjb's
$10^{-k-1}$ scaling to eliminate a division from the critical path. -
David Tolnay (@dtolnay) - Created and maintains the Rust port of Żmij, expanding the algorithm's reach and adoption in the Rust ecosystem.
-
Raffaello Giulietti - Author of the Schubfach algorithm, whose work forms a foundational basis for Żmij.
-
Yaoyuan Guo (@ibireme) - Author of the yy algorithm, whose ideas influenced key optimizations used in Żmij.
-
Junekey Jeon (@jk-jeon) - Author of the Dragonbox algorithm, which informed design and benchmarking comparisons for Żmij, as well as the
to_decimalAPI. -
Community contributors who provided feedback, issues, suggestions, and occasional commits, helping improve the robustness and performance of Żmij.