diff options
author | Jakub Jelinek <jakub@redhat.com> | 2022-11-01 13:40:05 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2022-11-01 13:40:05 +0100 |
commit | 0ae26533b3e26866f4130d40ec0659c273552643 (patch) | |
tree | b4a1ba28638599f3feb200f396480f9d8355dd08 /gcc | |
parent | 29ef50b6bc609130a1550cdfc3997d1e501ffb28 (diff) | |
download | gcc-0ae26533b3e26866f4130d40ec0659c273552643.zip gcc-0ae26533b3e26866f4130d40ec0659c273552643.tar.gz gcc-0ae26533b3e26866f4130d40ec0659c273552643.tar.bz2 |
libstdc++: std::to_chars std::{,b}float16_t support
The following patch on top of
https://gcc.gnu.org/pipermail/libstdc++/2022-October/054849.html
adds std::{,b}float16_t support for std::to_chars.
When precision is specified (or for std::bfloat16_t for hex mode even if not),
I believe we can just use the std::to_chars float (when float is mode
compatible with std::float32_t) overloads, both formats are proper subsets
of std::float32_t.
Unfortunately when precision is not specified and we are supposed to emit
shortest string, the std::{,b}float16_t strings are usually much shorter.
E.g. 1.e7p-14f16 shortest fixed representation is
0.0001161 and shortest scientific representation is
1.161e-04 while 1.e7p-14f32 (same number promoted to std::float32_t)
0.00011610985 and
1.1610985e-04.
Similarly for 1.38p-112bf16,
0.000000000000000000000000000000000235
2.35e-34 vs. 1.38p-112f32
0.00000000000000000000000000000000023472271
2.3472271e-34
For std::float16_t there are differences even in the shortest hex, say:
0.01p-14 vs. 1p-22
but only for denormal std::float16_t values (where all std::float16_t
denormals converted to std::float32_t are normal), __FLT16_MIN__ and
everything larger in absolute value than that is the same. Unless
that is a bug and we should try to discover shorter representations
even for denormals...
std::bfloat16_t has the same exponent range as std::float32_t, so all
std::bfloat16_t denormals are also std::float32_t denormals and thus
the shortest hex representations are the same.
As documented, ryu can handle arbitrary IEEE like floating point formats
(probably not wider than IEEE quad) using the generic_128 handling, but
ryu is hidden in libstdc++.so. As only few architectures support
std::float16_t right now and some of them have special ISA requirements
for those (e.g. on i?86 one needs -msse2) and std::bfloat16_t is right
now supported only on x86 (again with -msse2), perhaps with aarch64/arm
coming next if ARM is interested, but I think it is possible that more
will be added later, instead of exporting APIs from the library to handle
directly the std::{,b}float16_t overloads this patch instead exports
functions which take a float which is a superset of those and expects
the inline overloads to promote the 16-bit formats to 32-bit, then inside
of the library it ensures they are printed right.
With the added [[gnu::cold]] attribute because I think most users
will primarily use these formats as storage formats and perform arithmetics
in the excess precision for them and print also as std::float32_t the
added support doesn't seem to be too large, on x86_64:
readelf -Ws libstdc++.so.6.0.31 | grep float16_t
912: 00000000000ae824 950 FUNC GLOBAL DEFAULT 13 _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format@@GLIBCXX_3.4.31
5767: 00000000000ae4a1 899 FUNC GLOBAL DEFAULT 13 _ZSt20__to_chars_float16_tPcS_fSt12chars_format@@GLIBCXX_3.4.31
842: 000000000016d430 106 FUNC LOCAL DEFAULT 13 _ZN12_GLOBAL__N_113get_ieee_reprINS_23floating_type_float16_tEEENS_6ieee_tIT_EES3_
865: 0000000000170980 1613 FUNC LOCAL DEFAULT 13
+_ZSt23__floating_to_chars_hexIN12_GLOBAL__N_123floating_type_float16_tEESt15to_chars_resultPcS3_T_St8optionalIiE.constprop.0.isra.0
7205: 00000000000ae824 950 FUNC GLOBAL DEFAULT 13 _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format
7985: 00000000000ae4a1 899 FUNC GLOBAL DEFAULT 13 _ZSt20__to_chars_float16_tPcS_fSt12chars_format
so 3568 code bytes together or so.
Tested with the attached test (which doesn't prove the shortest
representation, just prints std::{,b}float16_t and std::float32_t
shortest strings side by side, then tries to verify it can be
emitted even into the exact sized range and can't be into range
one smaller than that and tries to read what is printed
back using from_chars float32_t overload (so there could be
double rounding, but apparently there is none for the shortest strings).
The only differences printed are for NaNs, where sNaNs are canonicalized
to canonical qNaNs and as to_chars doesn't print NaN mantissa, even qNaNs
other than the canonical one are read back just as the canonical NaN.
Also attaching what Patrick wrote to generate the pow10_adjustment_tab,
for std::float16_t only 1.0, 10.0, 100.0, 1000.0 and 10000.0 are powers
of 10 in the range because __FLT16_MAX__ is 65504.0, and all of the above
are exactly representable in std::float16_t, so we want to use 0 in
pow10_adjustment_tab.
2022-11-01 Jakub Jelinek <jakub@redhat.com>
* include/std/charconv (__to_chars_float16_t, __to_chars_bfloat16_t):
Declare.
(to_chars): Add _Float16 and __gnu_cxx::__bfloat16_t overloads.
* config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export
_ZSt20__to_chars_float16_tPcS_fSt12chars_format and
_ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format.
* src/c++17/floating_to_chars.cc (floating_type_float16_t,
floating_type_bfloat16_t): New types.
(floating_type_traits<floating_type_float16_t>,
floating_type_traits<floating_type_bfloat16_t>,
get_ieee_repr<floating_type_float16_t>,
get_ieee_repr<floating_type_bfloat16_t>,
__handle_special_value<floating_type_float16_t>,
__handle_special_value<floating_type_bfloat16_t>): New specializations.
(floating_to_shortest_scientific): Handle floating_type_float16_t
and floating_type_bfloat16_t like IEEE quad.
(__floating_to_chars_shortest): For floating_type_bfloat16_t call
__floating_to_chars_hex<float> rather than
__floating_to_chars_hex<floating_type_bfloat16_t> to avoid
instantiating the latter.
(__to_chars_float16_t, __to_chars_bfloat16_t): New functions.
Diffstat (limited to 'gcc')
0 files changed, 0 insertions, 0 deletions