Converting floats to strings quickly

When serializing data to JSON, CSV or when logging, we convert numbers to strings. Floating-point numbers are stored in binary, but we need them as decimal strings. The first formally published algorithm is Steele and White’s Dragon schemes (specifically Dragin2) in 1990. Since then, faster methods have emerged: Grisu3, Ryū, Schubfach, Grisu-Exact, and Dragonbox. In C++17, we have a standard function called std::to_chars for this purpose. A common objective is to generate the shortest strings while still being able to uniquely identify the original number.

We recently published Converting Binary Floating-Point Numbers to Shortest Decimal Strings. We examine the full conversion, from the floating-point number to the string. In practice, the conversion implies two steps: we take the number and compute the significant and the power of 10 (step 1) and then we generate the string (step 2). E.g., for the number pi, you might need to compute 31415927 and -7 (step 1) before generating the string 3.1415927. The string generation requires placing the dot at the right location and switching to the exponential notation when needed. The generation of the string is relatively cheap and was probably a negligible cost for older schemes, but as the software got faster, it is now a more important component (using 20% to 35% of the time).

The results vary quite a bit depending on the numbers being converted. But we find that the two implementations tend to do best: Dragonbox by Jeon and Schubfach by Giulietti. The Ryū implementation by Adams is close behind or just as fast. All of these techniques are about 10 times faster than the original Dragon 4 from 1990. A tenfold performance gain in performance over three decades is equivalent to a gain of about 8% per year, entirely due to better implementations and algorithms.

Efficient algorithms use between 200 and 350 instructions for each string generated. We find that the standard function std::to_chars under Linux uses slightly more instructions than needed (up to nearly 2 times too many). So there is room to improve common implementations. Using the popular C++ library fmt is slightly less efficient.

A fun fact is that we found that that none of the available functions generate the shortest possible string. The std::to_chars C++ function renders the number 0.00011 as 0.00011 (7 characters), while the shorter scientific form 1.1e-4 would do. But, by convention, when switching to the scientific notation, it is required to pad the exponent to two digits (so 1.1e-04). Beyond this technicality, we found that no implementation always generate the shortest string.

All our code, datasets, and raw results are open-source. The benchmarking suite is at https://github.com/fastfloat/float_serialization_benchmark, test data at https://github.com/fastfloat/float-data.

Reference: Converting Binary Floating-Point Numbers to Shortest
Decimal Strings: An Experimental Review
, Software: Practice and Experience (to appear)

Daniel Lemire, "Converting floats to strings quickly," in Daniel Lemire's blog, February 1, 2026, https://lemire.me/blog/2026/02/01/converting-floats-to-strings-quickly/.
[BibTeX]

Published by

Daniel Lemire

A computer science professor at the University of Quebec (TELUQ).

11 thoughts on “Converting floats to strings quickly”

    1. Thanks for the useful comment.

      Unfortunately, it takes time to write a paper and get it published, so we will always be slightly out of data. However, our benchmarking software is open.

        1. Victor:

          This code is not actually used (not in the cited paper). We started out testing fixed size outputs, but that’s not what we benchmarked.

          I will update the code, however.

  1. The fmt lib can have additional formatting. This is probably what makes it a little slower.

    I am personally a little annoyed by the requirement that the exponent needs to have (at least) two digits. With double precision you might even haven three digits. Some version of MSVC even changed this to always use three digits in the exponent. (What a waste of space!)

    I still have to support an old text-based file format where I need to output 10 digits and I need the best precision for this. Currently, we have our own algorithm running dragonbox in the background and then deciding on the formatting. It is impossible with the STL or fmt (last time I checked) to fix the maximum number of characters. Sometimes this uses more characters than specified. The exponent always having at least two digits is not helping with this either. Is there any good solution for this?

  2. A correction to Russ Cox’ post: macOS 26 libc does not use Eisel-Lemire. It uses an interval-arithmetic fast path that shares some concepts with Eisel-Lemire and Clinger but is a distinctly different algorithm. The same algorithm has also been implemented in Swift for the Swift standard library.

Leave a Reply

Your email address will not be published.

You can also subscribe by email to this blog (non-commercial, no ads, weekly email).

How to post code (C, C++, Java, Python, etc.):

Wrap your code in backticks, like this:

`int main() {
    return 0;
}`