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.133.124]) by sourceware.org (Postfix) with ESMTPS id D91D93858D26 for ; Mon, 7 Nov 2022 13:45:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org D91D93858D26 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=1667828717; 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=U+vdXmznCZyi6Fq7gFYI7NioHuZOYURhPjbPgfCZ0Y8=; b=FuXjV754PXjWVxK8g+ZDz5CmnI0IyjdcM0nQKs+H+MtQ/9QN+WQhHhaZDtG+YEUGKtyjjG 8sSfCcgELEPhHxXAvmF4f64yyyF0SBD2UQgQQWDoNXJBi/lpn4uiyzitCY+YOWS54uN9iD OlCXci/MA0lQ012T7GqroOsmkEdoPzM= Received: from mail-ej1-f69.google.com (mail-ej1-f69.google.com [209.85.218.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-122-6LiHg7ZENa-47b3TiUC2HQ-1; Mon, 07 Nov 2022 08:45:16 -0500 X-MC-Unique: 6LiHg7ZENa-47b3TiUC2HQ-1 Received: by mail-ej1-f69.google.com with SMTP id hp16-20020a1709073e1000b007adf5a83df7so6391261ejc.1 for ; Mon, 07 Nov 2022 05:45:16 -0800 (PST) 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=U+vdXmznCZyi6Fq7gFYI7NioHuZOYURhPjbPgfCZ0Y8=; b=mVhJOs4cGjtQiaAqEwpgMVaKZfq8UVpM/Hv67kbZav1aIKdCZqCgC1owtBa3rpBcs0 dZRIBsxS187VQnpsLmZkmNHIbN89B1uC61XlUAKnVzs5mBBKDComI15uI9ODuzrQH8+k PIgS9gY/JoIO4Gu/oDvZHqWNnFcx6/XYrF049yP3vbi2Tg0D2qVEeHyV6G9Bmnt4mjlv v3gK63qDExOvjM67zpRsT1XNFRNDV8EcfeUOpXfBUZvHM8c0UCe4fVnG4l1SAaEGvcOd yT6uMzbh129CpARu4r1oQnTAN5dG5EROnm4wfntCRfbFBgS4klsOnrE7Oyh30HeiyVEM 1Ffw== X-Gm-Message-State: ACrzQf3iMF0gccbZ4t6k1CFOxQrp/mqkkA0PAu4ehkBZKCguo+/c1Goi +LjXEOY94Jvyt6tv8e4bEut4YLJm/UmkK/ZN4xNnlpuaI3CEoiU/c7R2uhG7ntGGPvvjDROZcMR ESfsBfjxxkSHttjXhe/2KHeijevOj0ss= X-Received: by 2002:a05:6402:50d4:b0:461:e349:56b2 with SMTP id h20-20020a05640250d400b00461e34956b2mr51502014edb.17.1667828715439; Mon, 07 Nov 2022 05:45:15 -0800 (PST) X-Google-Smtp-Source: AMsMyM7od13e+gLGfn52H9Chx+wPNay6M2+PTGzj/7ry45bTY16xg5m9/qz6Hh+8BFvfoKEesRi3+wNLdTVKeHzJTjc= X-Received: by 2002:a05:6402:50d4:b0:461:e349:56b2 with SMTP id h20-20020a05640250d400b00461e34956b2mr51501984edb.17.1667828715077; Mon, 07 Nov 2022 05:45:15 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Jonathan Wakely Date: Mon, 7 Nov 2022 13:45:04 +0000 Message-ID: Subject: Re: [PATCH] libstdc++: Add _Float128 to_chars/from_chars support for x86, ia64 and ppc64le with glibc 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=-3.7 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=ham 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 Wed, 2 Nov 2022 at 09:26, Jakub Jelinek wrote: > > Hi! > > The following patch adds std::{to,from}_chars support for std::float128_t > on glibc 2.26+ for {i?86,x86_64,ia64,powerpc64le}-linux. > When long double is already IEEE quad, previous changes already handle > it by using long double overloads in _Float128 overloads. > The powerpc64le case (with explicit or implicit -mabi=ibmlongdouble) > is handled by using the __float128/__ieee128 entrypoints which are > already in the library and used for -mabi=ieeelongdouble. > For i?86, x86_64 and ia64 this patch adds new library entrypoints, > mostly by enabling the code that was already there for powerpc64le-linux. > Those use __float128 or __ieee128, the patch uses _Float128 for the > exported overloads and internally as template parameter. While > powerpc64le-linux uses __sprintfieee128 and __strtoieee128, > for _Float128 the patch uses the glibc 2.26 strfromf128 and strtof128 > APIs. So that one can build gcc against older glibc and then compile > user programs on newer glibc, the patch uses weak references unless > gcc is compiled against glibc 2.26+. strfromf128 unfortunately can't > handle %.0Lf and %.*Le, %.*Lf, %.*Lg format strings sprintf/__sprintfieee128 > use, we need to remove the L from those and replace * with actually > directly printing the precision into the format string (i.e. it can > handle %.0f and %.27f (floating point type is implied from the function > name)). Wow, nice work juggling all the pieces here. > Unlike the std::{,b}float16_t support, this one actually exports APIs > with std::float128_t aka _Float128 in the mangled name, because no > standard format is superset of it. On the other side, e.g. on i?86/x86_64 > it doesn't have restrictions like for _Float16/__bf16 which ISAs need > to be enabled in order to use it. > > The denorm_min case in the testcase is temporarily commented out because > of the ERANGE subnormal issue Patrick posted patch for. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? OK, thanks! > > 2022-11-02 Jakub Jelinek > > * include/std/charconv (from_chars, to_chars): Add _Float128 > overfloads if _GLIBCXX_HAVE_FLOAT128_MATH is defined. > * config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export > _ZSt8to_charsPcS_DF128_, _ZSt8to_charsPcS_DF128_St12chars_format, > _ZSt8to_charsPcS_DF128_St12chars_formati and > _ZSt10from_charsPKcS0_RDF128_St12chars_format. > * src/c++17/floating_from_chars.cc (USE_STRTOF128_FOR_FROM_CHARS): > Define if needed. > (__strtof128): Declare. > (from_chars_impl): Handle _Float128. > (from_chars): New _Float128 overload if USE_STRTOF128_FOR_FROM_CHARS > is define. > * src/c++17/floating_to_chars.cc (__strfromf128): Declare. > (FLOAT128_TO_CHARS): Define even when _Float128 is supported and > wider than long double. > (F128_type): Use _Float128 for that case. > (floating_type_traits): Specialize for F128_type rather than > __float128. > (sprintf_ld): Add length argument. Handle _Float128. > (__floating_to_chars_shortest, __floating_to_chars_precision): > Pass length to sprintf_ld. > (to_chars): Add _Float128 overloads for the F128_type being > _Float128 cases. > * testsuite/20_util/to_chars/float128_c++23.cc: New test. > > --- libstdc++-v3/include/std/charconv.jj 2022-10-31 22:20:39.475072806 +0100 > +++ libstdc++-v3/include/std/charconv 2022-11-01 16:48:50.693196228 +0100 > @@ -736,6 +736,27 @@ namespace __detail > __value = __val; > return __res; > } > +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH) > +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > + __extension__ from_chars_result > + from_chars(const char* __first, const char* __last, __ieee128& __value, > + chars_format __fmt = chars_format::general) noexcept; > + > + inline from_chars_result > + from_chars(const char* __first, const char* __last, _Float128& __value, > + chars_format __fmt = chars_format::general) noexcept > + { > + __extension__ __ieee128 __val; > + from_chars_result __res = from_chars(__first, __last, __val, __fmt); > + if (__res.ec == errc{}) > + __value = __val; > + return __res; > + } > +#else > + from_chars_result > + from_chars(const char* __first, const char* __last, _Float128& __value, > + chars_format __fmt = chars_format::general) noexcept; > +#endif > #endif > > #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) \ > @@ -851,6 +872,46 @@ namespace __detail > return to_chars(__first, __last, static_cast(__value), __fmt, > __precision); > } > +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH) > +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > + __extension__ to_chars_result > + to_chars(char* __first, char* __last, __float128 __value) noexcept; > + __extension__ to_chars_result > + to_chars(char* __first, char* __last, __float128 __value, > + chars_format __fmt) noexcept; > + __extension__ to_chars_result > + to_chars(char* __first, char* __last, __float128 __value, > + chars_format __fmt, int __precision) noexcept; > + > + inline to_chars_result > + to_chars(char* __first, char* __last, _Float128 __value) noexcept > + { > + __extension__ > + return to_chars(__first, __last, static_cast<__float128>(__value)); > + } > + inline to_chars_result > + to_chars(char* __first, char* __last, _Float128 __value, > + chars_format __fmt) noexcept > + { > + __extension__ > + return to_chars(__first, __last, static_cast<__float128>(__value), __fmt); > + } > + inline to_chars_result > + to_chars(char* __first, char* __last, _Float128 __value, > + chars_format __fmt, int __precision) noexcept > + { > + __extension__ > + return to_chars(__first, __last, static_cast<__float128>(__value), __fmt, > + __precision); > + } > +#else > + to_chars_result to_chars(char* __first, char* __last, _Float128 __value) > + noexcept; > + to_chars_result to_chars(char* __first, char* __last, _Float128 __value, > + chars_format __fmt) noexcept; > + to_chars_result to_chars(char* __first, char* __last, _Float128 __value, > + chars_format __fmt, int __precision) noexcept; > +#endif > #endif > > #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) > --- libstdc++-v3/config/abi/pre/gnu.ver.jj 2022-10-31 22:20:39.475072806 +0100 > +++ libstdc++-v3/config/abi/pre/gnu.ver 2022-11-01 17:00:57.682346445 +0100 > @@ -2450,6 +2450,10 @@ GLIBCXX_3.4.31 { > _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format; > _ZSt22__from_chars_float16_tPKcS0_RfSt12chars_format; > _ZSt23__from_chars_bfloat16_tPKcS0_RfSt12chars_format; > + _ZSt8to_charsPcS_DF128_; > + _ZSt8to_charsPcS_DF128_St12chars_format; > + _ZSt8to_charsPcS_DF128_St12chars_formati; > + _ZSt10from_charsPKcS0_RDF128_St12chars_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-10-31 22:20:39.476072793 +0100 > +++ libstdc++-v3/src/c++17/floating_from_chars.cc 2022-11-01 16:59:23.999615740 +0100 > @@ -59,6 +59,14 @@ > #endif > // strtold for __ieee128 > extern "C" __ieee128 __strtoieee128(const char*, char**); > +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ > + && defined(__GLIBC_PREREQ) > +#define USE_STRTOF128_FOR_FROM_CHARS 1 > +extern "C" _Float128 __strtof128(const char*, char**) > +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH > + __attribute__((__weak__)) > +#endif > + __asm ("strtof128"); > #endif > > #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \ > @@ -618,6 +626,16 @@ namespace > # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > else if constexpr (is_same_v) > tmpval = __strtoieee128(str, &endptr); > +# elif defined(USE_STRTOF128_FOR_FROM_CHARS) > + else if constexpr (is_same_v) > + { > +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH > + if (&__strtof128 == nullptr) > + tmpval = _Float128(std::strtold(str, &endptr); > + else > +#endif > + tmpval = __strtof128(str, &endptr); > + } > # endif > #else > tmpval = std::strtod(str, &endptr); > @@ -1232,6 +1250,14 @@ from_chars(const char* first, const char > chars_format fmt) noexcept > { > // fast_float doesn't support IEEE binary128 format, but we can use strtold. > + return from_chars_strtod(first, last, value, fmt); > +} > +#elif defined(USE_STRTOF128_FOR_FROM_CHARS) > +from_chars_result > +from_chars(const char* first, const char* last, _Float128& value, > + chars_format fmt) noexcept > +{ > + // fast_float doesn't support IEEE binary128 format, but we can use strtold. > return from_chars_strtod(first, last, value, fmt); > } > #endif > --- libstdc++-v3/src/c++17/floating_to_chars.cc.jj 2022-11-01 12:16:14.352652455 +0100 > +++ libstdc++-v3/src/c++17/floating_to_chars.cc 2022-11-01 16:25:48.330968909 +0100 > @@ -43,6 +43,13 @@ > #endif > // sprintf for __ieee128 > extern "C" int __sprintfieee128(char*, const char*, ...); > +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ > + && defined(__GLIBC_PREREQ) > +extern "C" int __strfromf128(char*, size_t, const char*, _Float128) > +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH > + __attribute__((__weak__)) > +#endif > + __asm ("strfromf128"); > #endif > > // This implementation crucially assumes float/double have the > @@ -77,10 +84,11 @@ extern "C" int __sprintfieee128(char*, c > #if defined _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT && __FLT128_MANT_DIG__ == 113 > // Define overloads of std::to_chars for __float128. > # define FLOAT128_TO_CHARS 1 > -#endif > - > -#ifdef FLOAT128_TO_CHARS > using F128_type = __float128; > +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \ > + && defined(__GLIBC_PREREQ) > +# define FLOAT128_TO_CHARS 1 > +using F128_type = _Float128; > #else > using F128_type = void; > #endif > @@ -252,7 +260,7 @@ namespace > > # ifdef FLOAT128_TO_CHARS > template<> > - struct floating_type_traits<__float128> : floating_type_traits_binary128 > + struct floating_type_traits : floating_type_traits_binary128 > { }; > # endif > #endif > @@ -1035,7 +1043,8 @@ namespace > #pragma GCC diagnostic ignored "-Wabi" > template > inline int > - sprintf_ld(char* buffer, const char* format_string, T value, Extra... args) > + sprintf_ld(char* buffer, size_t length __attribute__((unused)), > + const char* format_string, T value, Extra... args) > { > int len; > > @@ -1045,10 +1054,31 @@ namespace > fesetround(FE_TONEAREST); // We want round-to-nearest behavior. > #endif > > +#ifdef FLOAT128_TO_CHARS > #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > if constexpr (is_same_v) > len = __sprintfieee128(buffer, format_string, args..., value); > else > +#else > + if constexpr (is_same_v) > + { > +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH > + if (&__strfromf128 == nullptr) > + len = sprintf(buffer, format_string, args..., (long double)value); > + else > +#endif > + if constexpr (sizeof...(args) == 0) > + len = __strfromf128(buffer, length, "%.0f", value); > + else > + { > + // strfromf128 unfortunately doesn't allow .* > + char fmt[3 * sizeof(int) + 6]; > + sprintf(fmt, "%%.%d%c", args..., int(format_string[4])); > + len = __strfromf128(buffer, length, fmt, value); > + } > + } > + else > +#endif > #endif > len = sprintf(buffer, format_string, args..., value); > > @@ -1206,8 +1236,10 @@ template > // can avoid this if we use sprintf to write all but the last > // digit, and carefully compute and write the last digit > // ourselves. > - char buffer[expected_output_length+1]; > - const int output_length = sprintf_ld(buffer, "%.0Lf", value); > + char buffer[expected_output_length + 1]; > + const int output_length = sprintf_ld(buffer, > + expected_output_length + 1, > + "%.0Lf", value); > __glibcxx_assert(output_length == expected_output_length); > memcpy(first, buffer, output_length); > return {first + output_length, errc{}}; > @@ -1397,9 +1429,10 @@ template > __builtin_unreachable(); > > // Do the sprintf into the local buffer. > - char buffer[output_length_upper_bound+1]; > + char buffer[output_length_upper_bound + 1]; > int output_length > - = sprintf_ld(buffer, output_specifier, value, effective_precision); > + = sprintf_ld(buffer, output_length_upper_bound + 1, output_specifier, > + value, effective_precision); > __glibcxx_assert(output_length <= output_length_upper_bound); > > if (effective_precision > 0) > @@ -1799,6 +1832,7 @@ to_chars(char* first, char* last, long d > } > > #ifdef FLOAT128_TO_CHARS > +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT > to_chars_result > to_chars(char* first, char* last, __float128 value) noexcept > { > @@ -1817,6 +1851,26 @@ to_chars(char* first, char* last, __floa > { > return __floating_to_chars_precision(first, last, value, fmt, precision); > } > +#else > +to_chars_result > +to_chars(char* first, char* last, _Float128 value) noexcept > +{ > + return __floating_to_chars_shortest(first, last, value, chars_format{}); > +} > + > +to_chars_result > +to_chars(char* first, char* last, _Float128 value, chars_format fmt) noexcept > +{ > + return __floating_to_chars_shortest(first, last, value, fmt); > +} > + > +to_chars_result > +to_chars(char* first, char* last, _Float128 value, chars_format fmt, > + int precision) noexcept > +{ > + return __floating_to_chars_precision(first, last, value, fmt, precision); > +} > +#endif > #endif > > // Entrypoints for 16-bit floats. > --- libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc.jj 2022-11-01 17:04:19.895606130 +0100 > +++ libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc 2022-11-01 20:47:15.254989646 +0100 > @@ -0,0 +1,105 @@ > +// 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 > + > +#if defined(__STDCPP_FLOAT128_T__) \ > + && (defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) \ > + || defined(_GLIBCXX_HAVE_FLOAT128_MATH)) > +void > +test(std::chars_format fmt = std::chars_format{}) > +{ > + std::float128_t tests[] = { > +// std::numeric_limits::denorm_min(), > + std::numeric_limits::min(), > + 0.0f128, > + -42.0f128, > + 1234.5678912345f128, > + std::numbers::e_v, > + std::numbers::log2e_v, > + std::numbers::log10e_v, > + std::numbers::pi_v, > + std::numbers::inv_pi_v, > + std::numbers::inv_sqrtpi_v, > + std::numbers::ln2_v, > + std::numbers::ln10_v, > + std::numbers::sqrt2_v, > + std::numbers::sqrt3_v, > + std::numbers::inv_sqrt3_v, > + std::numbers::egamma_v, > + std::numbers::phi_v, > + std::numeric_limits::max() > + }; > + char str1[10000], str2[10000]; > + for (auto u : tests) > + { > + auto [ptr1, ec1] = std::to_chars(str1, str1 + sizeof(str1), u, fmt); > + VERIFY( ec1 == std::errc() ); > +// std::cout << i << ' ' << std::string_view (str1, ptr1) << '\n'; > + if (fmt == std::chars_format::fixed) > + { > + auto [ptr2, ec2] = std::to_chars(str2, str2 + (ptr1 - str1), u, fmt); > + VERIFY( ec2 == std::errc() && ptr2 - str2 == ptr1 - str1 ); > + auto [ptr3, ec3] = std::to_chars(str2, str2 + (ptr1 - str1 - 1), u, fmt); > + VERIFY( ec3 != std::errc() ); > + } > + std::float128_t v; > + auto [ptr4, ec4] = std::from_chars(str1, ptr1, v, > + fmt == std::chars_format{} > + ? std::chars_format::general : fmt); > + VERIFY( ec4 == std::errc() && ptr4 == ptr1 ); > + VERIFY( u == v ); > + > + auto [ptr5, ec5] = std::to_chars(str1, str1 + sizeof(str1), u, fmt, 90); > + VERIFY( ec5 == std::errc() ); > +// std::cout << i << ' ' << std::string_view (str1, ptr5) << '\n'; > + v = 4.0f128; > + auto [ptr6, ec6] = std::from_chars(str1, ptr5, v, > + fmt == std::chars_format{} > + ? std::chars_format::general : fmt); > + VERIFY( ec6 == std::errc() && ptr6 == ptr5 ); > + if (fmt == std::chars_format::fixed && u > 0.0f128 && u < 0.000001f128) > + VERIFY( v == 0.0 ); > + else > + VERIFY( u == v ); > + } > +} > +#endif > + > +int > +main() > +{ > +#if defined(__STDCPP_FLOAT128_T__) \ > + && (defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) \ > + || defined(_GLIBCXX_HAVE_FLOAT128_MATH)) > + test(); > + test(std::chars_format::fixed); > + test(std::chars_format::scientific); > + test(std::chars_format::general); > + test(std::chars_format::hex); > +#endif > +} > > Jakub >