diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 1c714fba0ae..4d97ec37147 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -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. diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv index e9bf953cdcc..09163af7fc9 100644 --- a/libstdc++-v3/include/std/charconv +++ b/libstdc++-v3/include/std/charconv @@ -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) diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 939c751f861..11a9be68770 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -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); @@ -1239,6 +1257,14 @@ from_chars(const char* first, const char* last, __ieee128& value, // 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 #endif // USE_LIB_FAST_FLOAT || USE_STRTOD_FOR_FROM_CHARS diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc index a253ee42416..d6be6465b12 100644 --- a/libstdc++-v3/src/c++17/floating_to_chars.cc +++ b/libstdc++-v3/src/c++17/floating_to_chars.cc @@ -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*, const char*, ...); #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 @@ -1034,7 +1042,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; @@ -1044,10 +1053,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); @@ -1205,8 +1235,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{}}; @@ -1396,9 +1428,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) @@ -1798,6 +1831,7 @@ to_chars(char* first, char* last, long double value, chars_format fmt, } #ifdef FLOAT128_TO_CHARS +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT to_chars_result to_chars(char* first, char* last, __float128 value) noexcept { @@ -1816,6 +1850,26 @@ to_chars(char* first, char* last, __float128 value, chars_format fmt, { 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. diff --git a/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc new file mode 100644 index 00000000000..4c01458e6d9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc @@ -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 +}