On Thu, 27 Oct 2022 at 09:00, Jakub Jelinek <ja...@redhat.com> wrote: > > Hi! > > 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. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? >
Unless I misunderstood something in Patrick's review, this is good and can be incrementally improved. OK for trunk, thanks.