“Floating-Point Printing and Parsing Can Be Simple And Fast”

The fastest known floating-point printer and parsing algorithms - fixed-width printing, shortest-width printing, and parsing, all in 400 lines of Go.

https://research.swtch.com/fp
https://research.swtch.com/fp-proof

research!rsc: Floating-Point Printing and Parsing Can Be Simple And Fast (Floating Point Formatting, Part 3)

@rsc have you seen https://vitaut.net/posts/2025/smallest-dtoa/ / https://vitaut.net/posts/2025/faster-dtoa/ / https://github.com/vitaut/zmij ? It's also pretty short and fast, and I didn't see it mentioned in your references section. (I haven't read your post in detail yet, apologies if it's mentioned somewhere!)

Also, the #perf link doesn't seem to work for me.

The smallest state-of-the-art double-to-string implementation

@thakis @rsc BTW zmij is already using a single multiplication by a power of 10 in the common case based on the work by Yaoyuan Guo. However, it looks like with this we can get rid of the fallback (Schubfach) which might simplify the implementation a bit.
@thakis @rsc Also unrounded logic seems similar to what Schubfach does - multiplication by 4 and sticky rounding (modified round to odd + shift). I am surprised it is not called out in the article. Maybe I missed something.
@vitaut @thakis Yes indeed. There's a paragraph about that in https://research.swtch.com/fp#related.short.
research!rsc: Floating-Point Printing and Parsing Can Be Simple And Fast (Floating Point Formatting, Part 3)

@rsc @thakis Thanks! I missed this section when reading last night but figured what’s going on on my own =)
@vitaut @thakis Hadn't seen the vitaut.net posts yet. Thanks very much - I will take a look.

@thakis @rsc it's not just perf; almost all of the links in the table of contents are broken. The things being linked to exist and have ids, just not the ones in the links.

the link to unround should be to unrounded_numbers,
fixedwidth -> fixed-width_printing (sic)
parsing -> parsing_decimals
short -> shortest-width_printing
fast -> fast_accurate_scaling
proof -> sketch_of_a_proof_of_fast_scaling
omit -> omit_needless_multiplications
perf -> performance
history -> related

But...nice work!

@bazzargh @thakis Fixed the internal links (and changed my software to find and complain about them before publishing). Thanks!
@rsc It only took 20 years of research to prove how simple this problem actually is.

@NohatCoder

The story is told of G. H. Hardy (and of other people) that during a lecture he said “It is obvious. . . Is it obvious?” left the room, and returned fifteen minutes later, saying “Yes, it’s obvious.” I was present once when Rogosinski asked Hardy whether the story were true. Hardy would admit only that he might have said “It’s obvious. . . Is it obvious?” (brief pause) “Yes, it’s obvious.”
— Ralph P. Boas, Jr., Lion Hunting and Other Mathematical Pursuits

😀

@rsc great work, thank you for sharing
@rsc an aside, but regarding the `bool2` function, is it recognized and handled properly due to the broader context? The reason I ask is that I seem to recall an older Go issue where the compiler will recognize assigning a local variable and returning that, but not this form with a conditional return. And it looks like it is still the case when compiled individually: https://godbolt.org/z/s3dcEfrxf
Compiler Explorer - Go (x86-64 gc 1.25.4)

package p // bool2 converts b to an integer: 1 for true, 0 for false. func bool2(b bool) int { if b { return 1 } return 0 } // I thought this format is required for Go compiler to remove it? func bool2Alt(b bool) (n int) { if b { n = 1 } return }

@inr Yes, it works when inlined. https://godbolt.org/z/97P368jq4
Compiler Explorer - Go (x86-64 gc 1.25.7)

package p // bool2 converts b to an integer: 1 for true, 0 for false. func bool2(b bool) int { if b{ return 1 } return 0 } func f(x, y int) int { return 5 + bool2(x==y) }