Zmij
A double-to-string conversion algorithm based on Schubfach and yy
Install / Use
/learn @vitaut/ZmijREADME
🐉 Żmij
A double-to-string conversion algorithm based on Schubfach and yy with implementations in C and C++
Features
- 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
Usage
#include "zmij.h"
#include <stdio.h>
int main() {
char buf[zmij::double_buffer_size + 1];
auto size = zmij::write(buf, sizeof(buf), 5.0507837461e-27);
buf[size] = '\0';
puts(buf);
}
Performance
Żmij v1 is more than 4x faster than Ryū used by multiple C++ standard library implementations, 9x faster than double-conversion and ~2.5x faster than Schubfach on dtoa-benchmark run on Apple M1.
| Function | Time (ns) | Speedup | |-------------------|----------:|--------:| | ostringstream | 871.431 | 1.00x | | sprintf | 735.292 | 1.19x | | double-conversion | 83.332 | 10.46x | | to_chars | 42.808 | 20.36x | | ryu | 36.809 | 23.67x | | schubfach | 24.721 | 35.25x | | fmt | 22.224 | 39.21x | | dragonbox | 20.532 | 42.44x | | yy | 14.006 | 62.22x | | xjb64 | 10.542 | 82.66x | | zmij | 8.661 | 100.62x | | null | 0.946 | 921.13x |
Conversion time (smaller is better):
<img width="726" height="313" alt="image" src="https://github.com/user-attachments/assets/c36c95c6-52a1-42a9-880e-d7621112f7d9" />
ostringstream and sprintf are excluded due to their significantly slower
performance.
<img width="739" height="605" alt="image" src="https://github.com/user-attachments/assets/e6452189-5a4c-4ba2-9e17-f720e263dd5d" />
On EPYC Milan (AMD64) running Linux, Żmij is approximately 2.8× faster than Ryū and 5× faster than double-conversion when compiled with GCC 11.5.
| Function | Time (ns) | Speedup | |---------------------|----------:|--------:| | ostringstream | 958.889 | 1.00x | | sprintf | 563.022 | 1.70x | | double-conversion | 95.706 | 10.02x | | to_chars | 67.115 | 14.29x | | ryu | 54.144 | 17.71x | | schubfach | 44.435 | 21.58x | | fmt | 40.098 | 23.91x | | dragonbox | 30.896 | 31.04x | | yy | 26.959 | 35.57x | | xjb64 | 19.275 | 49.75x | | zmij | 19.194 | 49.96x | | null | 2.766 | 346.72x |
<img width="741" height="327" alt="image" src="https://github.com/user-attachments/assets/e7f0ec3f-7317-4c60-b33d-c106215f1ee6" />
<img width="761" height="616" alt="image" src="https://github.com/user-attachments/assets/cece5726-006c-4712-bf09-6ca62c27ef29" />
Compile time
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.
Languages
- 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
Differences from Schubfach
- 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
Name
Ż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."
Acknowledgements
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.
-
Cassio Neri (@cassioneri) - Proposed the single-candidate rounding strategy 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. -
Russ Cox (@rsc) - Devised the simplified longer candidate selection for Schubfach adopted in Żmij's fallback path.
-
Community contributors who provided feedback, issues, suggestions, and occasional commits, helping improve the robustness and performance of Żmij.
Related Skills
node-connect
334.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
334.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.1kCommit, push, and open a PR
