P3908R0: constexpr from_chars<float> / to_chars<float>
This paper makes float-to-chars and chars-to-float conversion functionality in <charconv> constexpr compatible.
Motivation
This is the missing piece of <charconv>. Its absence forces users to write their own conversions and that's against the purpose of standardization. By allowing it, we will be able to reuse this code in constexpr formatting using std::format too.
Implementation
The implementation of the conversion algorithms can be done constexpr compatible with current language without resorting to any magic. Implementation in libc++ needs to just move the code from .cpp files to headers. Same thing applies for libstdc++.
Modern and fast algorithms
fast_float
It's a library done by Daniel Lemire which implements proposed functionality fully in constexpr, and it has over 1.9 stars on Github, it's used in production code.
Zmij
Recently a modern approach named Zmij was discovered by Victor Zverovich, it's only ~1kLoC. Modifying it to be constexpr showed there is no performance difference. It uses only integer operations and std::bit_cast.
In terms of compile time performance of just including this code, the additional time to including this header is negligable.
Compilation throughtput concerns
If the complation performance is still a concern, especially for a big tables, this can be improved to delay the instantiation by making it a template which is instantiated when first used (including the constant evaluation of its values). This can be done by template <typename = void> trick, which lazily instantiate the table only if it's needed. This is used in fast_float library already.
Alternative approach
Alternative approach is to provide builtins doing the same operations. They can be implemented with charconv code itself. Or compiler can detect and bypass from_chars / to_chars instead of using explicit builtins.
Wording
The change is simple adding (as usual) a few of constexpr here and there.
28.2.1 Header <charconv> synopsis [charconv.syn]
28.2.2 Primitive numeric output conversion [charconv.to.chars]
constexpr to_chars_result to_chars(char* first, char* last, integer-type value, int base = 10);
constexpr to_chars_result to_chars(char* first, char* last, floating-point-type value);
constexpr to_chars_result to_chars(char* first, char* last, floating-point-type value, chars_format fmt);
constexpr to_chars_result to_chars(char* first, char* last, floating-point-type value,
chars_format fmt, int precision);
28.2.3 Primitive numeric input conversion [charconv.from.chars]
constexpr from_chars_result from_chars(const char* first, const char* last,
integer-type& value, int base = 10);
constexpr from_chars_result from_chars(const char* first, const char* last, floating-point-type& value,
chars_format fmt = chars_format::general);
- the sign '+' may only appear in the exponent part;
- if fmt has chars_format::scientific set but not chars_format::fixed, the otherwise optional exponent part shall appear;
- if fmt has chars_format::fixed set but not chars_format::scientific, the optional exponent part shall not appear; and
- if fmt is chars_format::hex, the prefix "0x" or "0X" is assumed.
Feature test macro
17.3.2 Header <version> synopsis [version.syn]
#define __cpp_lib_constexpr_charconv 202207L20????L // // freestanding, also in <charconv>