From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 6AE7B385829C for ; Tue, 1 Nov 2022 12:29:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 6AE7B385829C Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1667305749; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=rPKefqXtlFwRSUYh/DnqS9dMYbw7yAtx/jTu1yrZOjI=; b=Iy2H6/+WJPG6MqwobkvOjwlslGhw0chDnS9k1pJVZCr3VdfBILGf5nqWbJ4KWSXsQekpwr pn/ecajbBHPoG16XeoMY/1pI+OXat4BKC1R+Pr+U+RjkT6cT3XNS2BUn7jcbA+TUrcD8sy vlJxogxIhjny3qX73ghhyC1ItkAOxDk= Received: from mail-ed1-f70.google.com (mail-ed1-f70.google.com [209.85.208.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-57-qonz1RT2Ok2lK6sJkS7IyA-1; Tue, 01 Nov 2022 08:29:07 -0400 X-MC-Unique: qonz1RT2Ok2lK6sJkS7IyA-1 Received: by mail-ed1-f70.google.com with SMTP id t4-20020a056402524400b004620845ba7bso9725528edd.4 for ; Tue, 01 Nov 2022 05:29:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=rPKefqXtlFwRSUYh/DnqS9dMYbw7yAtx/jTu1yrZOjI=; b=58Nn8d2njYaGgfhcgkazeutEj5Z7mr1BF2+kHE34Z0TgtA5JnN4v+Jv3Ur3Y8zW7Y+ q4o4zen9WPAsIeVX4pULgZiUAqWS32C0rlhfyx8sCJXah2C0RMFzVxYi9huVVXGpWdTj HFZd9vSi759odD11b/xCCet0nuA85/hEZEt2kbN0+/JpNjebruRXmyqZxLlVZVNw6bGd baoAPGXxI4fj4+SV7Wk9GSo89sW1IDiusH+nno4tFWU5sy7mQFXxkIGtNQTDWDVw/OxV bs0LtrguqKlUKhU71l45PC0oVsdXTuWB2D+Y2HllJt0J4rB3XfKTJ1WBpH5HwjSVBWlm pekw== X-Gm-Message-State: ACrzQf0Cnef+Azr6B1dN2aKmivd5aElsag2Hy1dAll5mET/K0iD50cJY 0yS7/DhgtxCeHjaDvt8O0T5dXXxG1A/L8oaRoMdPKbCGarvzRQQMSOcjb1+uuTgEZv7dMIWZoBh o75OFUH1LziMVvJHzhbcp7HCOSfn0xmY= X-Received: by 2002:a05:6402:50d4:b0:461:e349:56b2 with SMTP id h20-20020a05640250d400b00461e34956b2mr19217213edb.17.1667305746426; Tue, 01 Nov 2022 05:29:06 -0700 (PDT) X-Google-Smtp-Source: AMsMyM7hHMxrrMwtrffyn2KMWBuxnKhfTjhI2+DvCZXLPH05BOZXtsmA4EEgD8ehitGH9HRHQFXFlAnLqCjBjc5O8ZU= X-Received: by 2002:a05:6402:50d4:b0:461:e349:56b2 with SMTP id h20-20020a05640250d400b00461e34956b2mr19217185edb.17.1667305745979; Tue, 01 Nov 2022 05:29:05 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Jonathan Wakely Date: Tue, 1 Nov 2022 12:28:55 +0000 Message-ID: Subject: Re: [PATCH] libstdc++: std::from_chars std::{,b}float16_t support To: Jakub Jelinek Cc: Patrick Palka , gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-4.3 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Tue, 1 Nov 2022 at 09:36, Jakub Jelinek wrote: > > Hi! > > On top of the > https://gcc.gnu.org/pipermail/libstdc++/2022-October/054849.html > https://gcc.gnu.org/pipermail/libstdc++/2022-October/054886.html > the following patch adds std::from_chars support, similarly to the > previous std::to_chars patch through APIs that use float instead of > the 16-bit floating point formats as container. > The patch uses the fast_float library and doesn't need any changes > to it, like the previous patch it introduces wrapper classes around > float that represent the float holding float16_t or bfloat16_t value, > and specializes binary_format etc. from fast_float for these classes. > > The new test verifies exhaustively to_chars and from_chars afterward > results in the original value (except for nans) in all the fmt cases. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? OK, thanks. > > 2022-11-01 Jakub Jelinek > > * include/std/charconv (__from_chars_float16_t, > __from_chars_bfloat16_t): Declare. > (from_chars): Add _Float16 and __gnu_cxx::__bfloat16_t overloads. > * config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export > _ZSt22__from_chars_float16_tPKcS0_RfSt12chars_format and > _ZSt23__from_chars_bfloat16_tPKcS0_RfSt12chars_format. > * src/c++17/floating_from_chars.cc > (fast_float::floating_type_float16_t, > fast_float::floating_type_bfloat16_t): New classes. > (fast_float::binary_format, > fast_float::binary_format): New > specializations. > (fast_float::to_float, > fast_float::to_float, > fast_float::to_extended, > fast_float::to_extended): Likewise. > (fast_float::from_chars_16): New template function. > (__floating_from_chars_hex): Allow instantiation with > fast_float::floating_type_{,b}float16_t. > (from_chars): Formatting fixes for float/double/long double overloads. > (__from_chars_float16_t, __from_chars_bfloat16_t): New functions. > * testsuite/20_util/to_chars/float16_c++23.cc: New test. > > --- libstdc++-v3/include/std/charconv.jj 2022-10-28 11:15:40.113959052 +0200 > +++ libstdc++-v3/include/std/charconv 2022-10-28 11:28:04.172657801 +0200 > @@ -673,6 +673,32 @@ namespace __detail > from_chars(const char* __first, const char* __last, long double& __value, > chars_format __fmt = chars_format::general) noexcept; > > + // Library routines for 16-bit extended floating point formats > + // using float as interchange format. > + from_chars_result > + __from_chars_float16_t(const char* __first, const char* __last, > + float& __value, > + chars_format __fmt = chars_format::general) noexcept; > + from_chars_result > + __from_chars_bfloat16_t(const char* __first, const char* __last, > + float& __value, > + chars_format __fmt = chars_format::general) noexcept; > + > +#if defined(__STDCPP_FLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) \ > + && defined(__cpp_lib_to_chars) > + inline from_chars_result > + from_chars(const char* __first, const char* __last, _Float16& __value, > + chars_format __fmt = chars_format::general) noexcept > + { > + float __val; > + from_chars_result __res > + = __from_chars_float16_t(__first, __last, __val, __fmt); > + if (__res.ec == errc{}) > + __value = __val; > + return __res; > + } > +#endif > + > #if defined(__STDCPP_FLOAT32_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) > inline from_chars_result > from_chars(const char* __first, const char* __last, _Float32& __value, > @@ -709,6 +735,22 @@ namespace __detail > if (__res.ec == errc{}) > __value = __val; > return __res; > + } > +#endif > + > +#if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) \ > + && defined(__cpp_lib_to_chars) > + inline from_chars_result > + from_chars(const char* __first, const char* __last, > + __gnu_cxx::__bfloat16_t & __value, > + chars_format __fmt = chars_format::general) noexcept > + { > + float __val; > + from_chars_result __res > + = __from_chars_bfloat16_t(__first, __last, __val, __fmt); > + if (__res.ec == errc{}) > + __value = __val; > + return __res; > } > #endif > #endif > --- libstdc++-v3/config/abi/pre/gnu.ver.jj 2022-10-28 11:15:40.115959024 +0200 > +++ libstdc++-v3/config/abi/pre/gnu.ver 2022-10-28 16:55:55.274849390 +0200 > @@ -2448,6 +2448,8 @@ GLIBCXX_3.4.31 { > _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE15_M_replace_cold*; > _ZSt20__to_chars_float16_tPcS_fSt12chars_format; > _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format; > + _ZSt22__from_chars_float16_tPKcS0_RfSt12chars_format; > + _ZSt23__from_chars_bfloat16_tPKcS0_RfSt12chars_format; > } GLIBCXX_3.4.30; > > # Symbols in the support library (libsupc++) have their own tag. > --- libstdc++-v3/src/c++17/floating_from_chars.cc.jj 2022-05-23 21:44:49.107846783 +0200 > +++ libstdc++-v3/src/c++17/floating_from_chars.cc 2022-10-31 15:06:30.338480517 +0100 > @@ -75,6 +75,272 @@ extern "C" __ieee128 __strtoieee128(cons > namespace > { > # include "fast_float/fast_float.h" > + > +namespace fast_float > +{ > + > + // Wrappers around float for std::{,b}float16_t promoted to float. > + struct floating_type_float16_t > + { > + float* x; > + uint16_t bits; > + }; > + struct floating_type_bfloat16_t > + { > + float* x; > + uint16_t bits; > + }; > + > + template<> > + constexpr int > + binary_format::mantissa_explicit_bits() > + { return 10; } > + > + template<> > + constexpr int > + binary_format::mantissa_explicit_bits() > + { return 7; } > + > + // 10 bits of stored mantissa, pow(5,q) <= 0x4p+10 implies q <= 5 > + template<> > + constexpr int > + binary_format::max_exponent_round_to_even() > + { return 5; } > + > + // 7 bits of stored mantissa, pow(5,q) <= 0x4p+7 implies q <= 3 > + template<> > + constexpr int > + binary_format::max_exponent_round_to_even() > + { return 3; } > + > + // 10 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+11 implies q >= -22 > + template<> > + constexpr int > + binary_format::min_exponent_round_to_even() > + { return -22; } > + > + // 7 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+8 implies q >= -24 > + template<> > + constexpr int > + binary_format::min_exponent_round_to_even() > + { return -24; } > + > + template<> > + constexpr int > + binary_format::minimum_exponent() > + { return -15; } > + > + template<> > + constexpr int > + binary_format::minimum_exponent() > + { return -127; } > + > + template<> > + constexpr int > + binary_format::infinite_power() > + { return 0x1F; } > + > + template<> > + constexpr int > + binary_format::infinite_power() > + { return 0xFF; } > + > + template<> > + constexpr int > + binary_format::sign_index() > + { return 15; } > + > + template<> > + constexpr int > + binary_format::sign_index() > + { return 15; } > + > + template<> > + constexpr int > + binary_format::largest_power_of_ten() > + { return 4; } > + > + template<> > + constexpr int > + binary_format::largest_power_of_ten() > + { return 38; } > + > + template<> > + constexpr int > + binary_format::smallest_power_of_ten() > + { return -27; } > + > + template<> > + constexpr int > + binary_format::smallest_power_of_ten() > + { return -60; } > + > + template<> > + constexpr size_t > + binary_format::max_digits() > + { return 22; } > + > + template<> > + constexpr size_t > + binary_format::max_digits() > + { return 98; } > + > + // negative_digit_comp converts adjusted_mantissa to the (originally only) > + // floating type and immediately back with slight tweaks (e.g. explicit > + // leading bit instead of implicit for normals). > + // Avoid going through the floating point type. > + template<> > + fastfloat_really_inline void > + to_float(bool negative, adjusted_mantissa am, > + floating_type_float16_t &value) > + { > + constexpr int mantissa_bits > + = binary_format::mantissa_explicit_bits(); > + value.bits = (am.mantissa > + | (uint16_t(am.power2) << mantissa_bits) > + | (negative ? 0x8000 : 0)); > + } > + > + template<> > + fastfloat_really_inline void > + to_float(bool negative, adjusted_mantissa am, > + floating_type_bfloat16_t &value) > + { > + constexpr int mantissa_bits > + = binary_format::mantissa_explicit_bits(); > + value.bits = (am.mantissa > + | (uint16_t(am.power2) << mantissa_bits) > + | (negative ? 0x8000 : 0)); > + } > + > + template <> > + fastfloat_really_inline adjusted_mantissa > + to_extended(floating_type_float16_t value) noexcept > + { > + adjusted_mantissa am; > + constexpr int mantissa_bits > + = binary_format::mantissa_explicit_bits(); > + int32_t bias > + = (mantissa_bits > + - binary_format::minimum_exponent()); > + constexpr uint16_t exponent_mask = 0x7C00; > + constexpr uint16_t mantissa_mask = 0x03FF; > + constexpr uint16_t hidden_bit_mask = 0x0400; > + if ((value.bits & exponent_mask) == 0) { > + // denormal > + am.power2 = 1 - bias; > + am.mantissa = value.bits & mantissa_mask; > + } else { > + // normal > + am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits); > + am.power2 -= bias; > + am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask; > + } > + return am; > + } > + > + template <> > + fastfloat_really_inline adjusted_mantissa > + to_extended(floating_type_bfloat16_t value) noexcept > + { > + adjusted_mantissa am; > + constexpr int mantissa_bits > + = binary_format::mantissa_explicit_bits(); > + int32_t bias > + = (mantissa_bits > + - binary_format::minimum_exponent()); > + constexpr uint16_t exponent_mask = 0x7F80; > + constexpr uint16_t mantissa_mask = 0x007F; > + constexpr uint16_t hidden_bit_mask = 0x0080; > + if ((value.bits & exponent_mask) == 0) { > + // denormal > + am.power2 = 1 - bias; > + am.mantissa = value.bits & mantissa_mask; > + } else { > + // normal > + am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits); > + am.power2 -= bias; > + am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask; > + } > + return am; > + } > + > + // Like fast_float.h from_chars_advanced, but for 16-bit float. > + template > + from_chars_result > + from_chars_16(const char* first, const char* last, T &value, > + chars_format fmt) noexcept > + { > + parse_options options{fmt}; > + > + from_chars_result answer; > + if (first == last) > + { > + answer.ec = std::errc::invalid_argument; > + answer.ptr = first; > + return answer; > + } > + > + parsed_number_string pns = parse_number_string(first, last, options); > + if (!pns.valid) > + return detail::parse_infnan(first, last, *value.x); > + > + answer.ec = std::errc(); > + answer.ptr = pns.lastmatch; > + > + adjusted_mantissa am > + = compute_float>(pns.exponent, pns.mantissa); > + if (pns.too_many_digits && am.power2 >= 0) > + { > + if (am != compute_float>(pns.exponent, > + pns.mantissa + 1)) > + am = compute_error>(pns.exponent, pns.mantissa); > + } > + > + // If we called compute_float>(pns.exponent, pns.mantissa) > + // and we have an invalid power (am.power2 < 0), > + // then we need to go the long way around again. This is very uncommon. > + if (am.power2 < 0) > + am = digit_comp(pns, am); > + > + if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) > + || am.power2 == binary_format::infinite_power()) > + { > + // In case of over/underflow, return result_out_of_range and don't > + // modify value, as per [charconv.from.chars]/1. Note that LWG 3081 wants > + // to modify value in this case too. > + answer.ec = std::errc::result_out_of_range; > + return answer; > + } > + > + // Transform the {,b}float16_t to float32_t before to_float. > + if constexpr (std::is_same_v) > + { > + if (am.power2 == 0) > + { > + if (am.mantissa) > + { > + int n = (std::numeric_limits::digits > + - __builtin_clz (am.mantissa)) - 1; > + am.mantissa &= ~(static_cast(1) << n); > + am.mantissa <<= (binary_format::mantissa_explicit_bits() > + - n); > + am.power2 = n + 0x67; > + } > + } > + else > + { > + am.mantissa <<= 13; > + am.power2 += 0x70; > + } > + } > + else > + am.mantissa <<= 16; > + to_float(pns.negative, am, *value.x); > + return answer; > + } > +} // fast_float > + > } // anon namespace > #endif > > @@ -490,11 +756,14 @@ namespace > from_chars_result > __floating_from_chars_hex(const char* first, const char* last, T& value) > { > - static_assert(is_same_v || is_same_v); > - > - using uint_t = conditional_t, uint32_t, uint64_t>; > - constexpr int mantissa_bits = is_same_v ? 23 : 52; > - constexpr int exponent_bits = is_same_v ? 8 : 11; > + using uint_t = conditional_t, uint32_t, > + conditional_t, uint64_t, > + uint16_t>>; > + constexpr int mantissa_bits > + = fast_float::binary_format::mantissa_explicit_bits(); > + constexpr int exponent_bits > + = is_same_v ? 11 > + : is_same_v ? 5 : 8; > constexpr int exponent_bias = (1 << (exponent_bits - 1)) - 1; > > __glibcxx_requires_valid_range(first, last); > @@ -520,12 +789,21 @@ namespace > if (starts_with_ci(first, last, "inity"sv)) > first += strlen("inity"); > > - uint_t result = 0; > - result |= sign_bit; > - result <<= exponent_bits; > - result |= (1ull << exponent_bits) - 1; > - result <<= mantissa_bits; > - memcpy(&value, &result, sizeof(result)); > + if constexpr (is_same_v || is_same_v) > + { > + uint_t result = 0; > + result |= sign_bit; > + result <<= exponent_bits; > + result |= (1ull << exponent_bits) - 1; > + result <<= mantissa_bits; > + memcpy(&value, &result, sizeof(result)); > + } > + else > + { > + // float +/-Inf. > + uint32_t result = 0x7F800000 | (sign_bit ? 0x80000000U : 0); > + memcpy(value.x, &result, sizeof(result)); > + } > > return {first, errc{}}; > } > @@ -566,12 +844,21 @@ namespace > > // We make the implementation-defined decision of ignoring the > // sign bit and the n-char-sequence when assembling the NaN. > - uint_t result = 0; > - result <<= exponent_bits; > - result |= (1ull << exponent_bits) - 1; > - result <<= mantissa_bits; > - result |= (1ull << (mantissa_bits - 1)) | 1; > - memcpy(&value, &result, sizeof(result)); > + if constexpr (is_same_v || is_same_v) > + { > + uint_t result = 0; > + result <<= exponent_bits; > + result |= (1ull << exponent_bits) - 1; > + result <<= mantissa_bits; > + result |= (1ull << (mantissa_bits - 1)) | 1; > + memcpy(&value, &result, sizeof(result)); > + } > + else > + { > + // float qNaN. > + uint32_t result = 0x7FC00001; > + memcpy(value.x, &result, sizeof(result)); > + } > > return {first, errc{}}; > } > @@ -633,18 +920,27 @@ namespace > mantissa |= uint_t(hexit) << mantissa_idx; > else if (mantissa_idx >= -4) > { > - if constexpr (is_same_v) > + if constexpr (is_same_v > + || is_same_v + fast_float::floating_type_bfloat16_t>) > { > __glibcxx_assert(mantissa_idx == -1); > mantissa |= hexit >> 1; > midpoint_bit = (hexit & 0b0001) != 0; > } > - else > + else if constexpr (is_same_v) > { > __glibcxx_assert(mantissa_idx == -4); > midpoint_bit = (hexit & 0b1000) != 0; > nonzero_tail = (hexit & 0b0111) != 0; > } > + else > + { > + __glibcxx_assert(mantissa_idx == -2); > + mantissa |= hexit >> 2; > + midpoint_bit = (hexit & 0b0010) != 0; > + nonzero_tail = (hexit & 0b0001) != 0; > + } > } > else > nonzero_tail |= (hexit != 0x0); > @@ -808,7 +1104,34 @@ namespace > __glibcxx_assert(((mantissa & (1ull << mantissa_bits)) != 0) > == (biased_exponent != 0)); > } > - memcpy(&value, &result, sizeof(result)); > + if constexpr (is_same_v || is_same_v) > + memcpy(&value, &result, sizeof(result)); > + else if constexpr (is_same_v) > + { > + uint32_t res = uint32_t{result} << 16; > + memcpy(value.x, &res, sizeof(res)); > + } > + else > + { > + // Otherwise float16_t which needs to be converted to float32_t. > + uint32_t res; > + if ((result & 0x7FFF) == 0) > + res = uint32_t{result} << 16; // +/-0.0f16 > + else if ((result & 0x7C00) == 0) > + { // denormal > + unsigned n = (std::numeric_limits::digits > + - __builtin_clz (result & 0x3FF) - 1); > + res = uint32_t{result} & 0x3FF & ~(uint32_t{1} << n); > + res <<= 23 - n; > + res |= (((uint32_t{n} + 0x67) << 23) > + | ((uint32_t{result} & 0x8000) << 16)); > + } > + else > + res = (((uint32_t{result} & 0x3FF) << 13) > + | ((((uint32_t{result} >> 10) & 0x1F) + 0x70) << 23) > + | ((uint32_t{result} & 0x8000) << 16)); > + memcpy(value.x, &res, sizeof(res)); > + } > > return {first, errc{}}; > } > @@ -826,9 +1149,7 @@ from_chars(const char* first, const char > if (fmt == chars_format::hex) > return __floating_from_chars_hex(first, last, value); > else > - { > - return fast_float::from_chars(first, last, value, fmt); > - } > + return fast_float::from_chars(first, last, value, fmt); > #else > return from_chars_strtod(first, last, value, fmt); > #endif > @@ -842,9 +1163,7 @@ from_chars(const char* first, const char > if (fmt == chars_format::hex) > return __floating_from_chars_hex(first, last, value); > else > - { > - return fast_float::from_chars(first, last, value, fmt); > - } > + return fast_float::from_chars(first, last, value, fmt); > #else > return from_chars_strtod(first, last, value, fmt); > #endif > @@ -863,9 +1182,7 @@ from_chars(const char* first, const char > if (fmt == chars_format::hex) > result = __floating_from_chars_hex(first, last, dbl_value); > else > - { > - result = fast_float::from_chars(first, last, dbl_value, fmt); > - } > + result = fast_float::from_chars(first, last, dbl_value, fmt); > if (result.ec == errc{}) > value = dbl_value; > return result; > @@ -874,6 +1191,31 @@ from_chars(const char* first, const char > #endif > } > > +#if USE_LIB_FAST_FLOAT > +// Entrypoints for 16-bit floats. > +[[gnu::cold]] from_chars_result > +__from_chars_float16_t(const char* first, const char* last, float& value, > + chars_format fmt) noexcept > +{ > + struct fast_float::floating_type_float16_t val{ &value, 0 }; > + if (fmt == chars_format::hex) > + return __floating_from_chars_hex(first, last, val); > + else > + return fast_float::from_chars_16(first, last, val, fmt); > +} > + > +[[gnu::cold]] from_chars_result > +__from_chars_bfloat16_t(const char* first, const char* last, float& value, > + chars_format fmt) noexcept > +{ > + struct fast_float::floating_type_bfloat16_t val{ &value, 0 }; > + if (fmt == chars_format::hex) > + return __floating_from_chars_hex(first, last, val); > + else > + return fast_float::from_chars_16(first, last, val, fmt); > +} > +#endif > + > #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT > // Make std::from_chars for 64-bit long double an alias for the overload > // for double. > --- libstdc++-v3/testsuite/20_util/to_chars/float16_c++23.cc.jj 2022-10-31 16:09:43.486130628 +0100 > +++ libstdc++-v3/testsuite/20_util/to_chars/float16_c++23.cc 2022-10-31 16:27:00.690851973 +0100 > @@ -0,0 +1,76 @@ > +// Copyright (C) 2022 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// . > + > +// { dg-options "-std=gnu++2b" } > +// { dg-do run { target c++23 } } > +// { dg-require-effective-target ieee_floats } > +// { dg-require-effective-target size32plus } > +// { dg-add-options ieee } > + > +#include > +#include > +#include > +#include > +#include > + > +template > +void > +test(std::chars_format fmt = std::chars_format{}) > +{ > + char str1[128], str2[128], str3[128]; > + union U { unsigned short s; T f; } u, v; > + for (int i = 0; i <= (unsigned short) ~0; ++i) > + { > + u.s = i; > + auto [ptr1, ec1] = std::to_chars(str1, str1 + sizeof(str1), u.f, fmt); > + auto [ptr2, ec2] = std::to_chars(str2, str2 + sizeof(str2), std::float32_t(u.f), fmt); > + VERIFY( ec1 == std::errc() && ec2 == std::errc()); > +// std::cout << i << ' ' << std::string_view (str1, ptr1) > +// << '\t' << std::string_view (str2, ptr2) << '\n'; > + if (fmt == std::chars_format::fixed) > + { > + auto [ptr3, ec3] = std::to_chars(str3, str3 + (ptr1 - str1), u.f, fmt); > + VERIFY( ec3 == std::errc() && ptr3 - str3 == ptr1 - str1 ); > + auto [ptr4, ec4] = std::to_chars(str3, str3 + (ptr1 - str1 - 1), u.f, fmt); > + VERIFY( ec4 != std::errc() ); > + } > + auto [ptr5, ec5] = std::from_chars(str1, ptr1, v.f, > + fmt == std::chars_format{} > + ? std::chars_format::general : fmt); > + VERIFY( ec5 == std::errc() && ptr5 == ptr1 ); > + VERIFY( u.s == v.s || (std::isnan(u.f) && std::isnan(v.f)) ); > + } > +} > + > +int > +main() > +{ > +#ifdef __STDCPP_FLOAT16_T__ > + test(); > + test(std::chars_format::fixed); > + test(std::chars_format::scientific); > + test(std::chars_format::general); > + test(std::chars_format::hex); > +#endif > +#ifdef __STDCPP_BFLOAT16_T__ > + test(); > + test(std::chars_format::fixed); > + test(std::chars_format::scientific); > + test(std::chars_format::general); > + test(std::chars_format::hex); > +#endif > +} > > Jakub >