From e5bcbcd04cfcb2f8635ea8431f4e77065e44b0bd Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 7 Nov 2022 15:15:50 +0100 Subject: libstdc++: Add _Float128 to_chars/from_chars support for x86, ia64 and ppc64le with glibc 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)). 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. 2022-11-07 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/config/abi/pre/gnu.ver | 4 + libstdc++-v3/include/std/charconv | 61 ++++++++++++ libstdc++-v3/src/c++17/floating_from_chars.cc | 26 +++++ libstdc++-v3/src/c++17/floating_to_chars.cc | 72 ++++++++++++-- .../testsuite/20_util/to_chars/float128_c++23.cc | 105 +++++++++++++++++++++ 5 files changed, 259 insertions(+), 9 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 1c714fb..4d97ec3 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 e9bf953..09163af 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 939c751..11a9be6 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 a253ee4..d6be646 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 0000000..4c01458 --- /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 +} -- cgit v1.1 From cb0ceeaee9e041aaac3edd089b07b439621d0f29 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Mon, 7 Nov 2022 15:17:21 +0100 Subject: libstdc++: Update from latest fast_float [PR107468] The following patch updates from fast_float trunk. That way it grabs two of the 4 LOCAL_PATCHES, some smaller tweaks, to_extended cleanups and most importantly fix for the incorrect rounding case, PR107468 aka https://github.com/fastfloat/fast_float/issues/149 Using std::fegetround showed in benchmarks too slow, so instead of doing that the patch limits the fast path where it uses floating point multiplication rather than integral to cases where we can prove there will be no rounding (the multiplication will be exact, not just that the two multiplication or division operation arguments are exactly representable). 2022-11-07 Jakub Jelinek PR libstdc++/107468 * src/c++17/fast_float/MERGE: Adjust for merge from upstream. * src/c++17/fast_float/LOCAL_PATCHES: Remove commits that were upstreamed. * src/c++17/fast_float/README.md: Merge from fast_float 662497742fea7055f0e0ee27e5a7ddc382c2c38e commit. * src/c++17/fast_float/fast_float.h: Likewise. * testsuite/20_util/from_chars/pr107468.cc: New test. --- libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES | 2 - libstdc++-v3/src/c++17/fast_float/MERGE | 2 +- libstdc++-v3/src/c++17/fast_float/README.md | 91 +++++++---- libstdc++-v3/src/c++17/fast_float/fast_float.h | 173 +++++++++++++-------- .../testsuite/20_util/from_chars/pr107468.cc | 42 +++++ 5 files changed, 211 insertions(+), 99 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES b/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES index 5bb4293..71495d6 100644 --- a/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES +++ b/libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES @@ -1,4 +1,2 @@ r12-6647 r12-6648 -r12-6664 -r12-6665 diff --git a/libstdc++-v3/src/c++17/fast_float/MERGE b/libstdc++-v3/src/c++17/fast_float/MERGE index 43bdc39..20eae9d 100644 --- a/libstdc++-v3/src/c++17/fast_float/MERGE +++ b/libstdc++-v3/src/c++17/fast_float/MERGE @@ -1,4 +1,4 @@ -d35368cae610b4edeec61cd41e4d2367a4d33f58 +662497742fea7055f0e0ee27e5a7ddc382c2c38e The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libstdc++-v3/src/c++17/fast_float/README.md b/libstdc++-v3/src/c++17/fast_float/README.md index 1e1c06d..d6ae279 100644 --- a/libstdc++-v3/src/c++17/fast_float/README.md +++ b/libstdc++-v3/src/c++17/fast_float/README.md @@ -1,12 +1,5 @@ ## fast_float number parsing library: 4x faster than strtod -![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg) -![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg) -![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg) -![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg) -![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg) -[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml) - The fast_float library provides fast header-only implementations for the C++ from_chars functions for `float` and `double` types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including @@ -28,8 +21,8 @@ struct from_chars_result { ``` It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting -a locale-independent format equivalent to the C++17 from_chars function. -The resulting floating-point value is the closest floating-point values (using either float or double), +a locale-independent format equivalent to the C++17 from_chars function. +The resulting floating-point value is the closest floating-point values (using either float or double), using the "round to even" convention for values that would otherwise fall right in-between two values. That is, we provide exact parsing according to the IEEE standard. @@ -47,7 +40,7 @@ Example: ``` C++ #include "fast_float/fast_float.h" #include - + int main() { const std::string input = "3.1416 xyz "; double result; @@ -60,15 +53,15 @@ int main() { Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of -the type `fast_float::chars_format`. It is a bitset value: we check whether +the type `fast_float::chars_format`. It is a bitset value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set to determine whether we allow the fixed point and scientific notation respectively. The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. -The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. +The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. * The `from_chars` function does not skip leading white-space characters. * [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden. -* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. +* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. Furthermore, we have the following restrictions: * We only support `float` and `double` types at this time. @@ -77,22 +70,22 @@ Furthermore, we have the following restrictions: We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. - +We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`). ## Using commas as decimal separator The C++ standard stipulate that `from_chars` has to be locale-independent. In -particular, the decimal separator has to be the period (`.`). However, -some users still want to use the `fast_float` library with in a locale-dependent +particular, the decimal separator has to be the period (`.`). However, +some users still want to use the `fast_float` library with in a locale-dependent manner. Using a separate function called `from_chars_advanced`, we allow the users -to pass a `parse_options` instance which contains a custom decimal separator (e.g., +to pass a `parse_options` instance which contains a custom decimal separator (e.g., the comma). You may use it as follows. ```C++ #include "fast_float/fast_float.h" #include - + int main() { const std::string input = "3,1416 xyz "; double result; @@ -104,25 +97,55 @@ int main() { } ``` +You can parse delimited numbers: +```C++ + const std::string input = "234532.3426362,7869234.9823,324562.645"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 234532.3426362. + if(answer.ptr[0] != ',') { + // unexpected delimiter + } + answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 7869234.9823. + if(answer.ptr[0] != ',') { + // unexpected delimiter + } + answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result); + if(answer.ec != std::errc()) { + // check error + } + // we have result == 324562.645. +``` ## Reference -- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021. +- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021. ## Other programming languages - [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. - [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. -- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. +- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core). - [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. ## Relation With Other Work -The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). +The fast_float library is part of GCC (as of version 12): the `from_chars` function in GCC relies on fast_float. + +The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). +There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS). + ## Users The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). @@ -135,14 +158,14 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems. ``` -$ ./build/benchmarks/benchmark +$ ./build/benchmarks/benchmark # parsing random integers in the range [0,1) -volume = 2.09808 MB -netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s -doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s -strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s -abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s -fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s +volume = 2.09808 MB +netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s +doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s +strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s +abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s +fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s ``` See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code. @@ -183,23 +206,23 @@ You should change the `GIT_TAG` line so that you recover the version you wish to ## Using as single header -The script `script/amalgamate.py` may be used to generate a single header +The script `script/amalgamate.py` may be used to generate a single header version of the library if so desired. -Just run the script from the root directory of this repository. +Just run the script from the root directory of this repository. You can customize the license type and output file if desired as described in the command line help. You may directly download automatically generated single-header files: -https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h +https://github.com/fastfloat/fast_float/releases/download/v3.4.0/fast_float.h ## Credit -Though this work is inspired by many different people, this work benefited especially from exchanges with -Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided +Though this work is inspired by many different people, this work benefited especially from exchanges with +Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits. -The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published +The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published under the Apache 2.0 license. ## License diff --git a/libstdc++-v3/src/c++17/fast_float/fast_float.h b/libstdc++-v3/src/c++17/fast_float/fast_float.h index 31fb88b..5da55e2 100644 --- a/libstdc++-v3/src/c++17/fast_float/fast_float.h +++ b/libstdc++-v3/src/c++17/fast_float/fast_float.h @@ -74,7 +74,7 @@ struct parse_options { * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of * the type `fast_float::chars_format`. It is a bitset value: we check whether * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set - * to determine whether we allowe the fixed point and scientific notation respectively. + * to determine whether we allow the fixed point and scientific notation respectively. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. */ template @@ -98,12 +98,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last, || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ || defined(__MINGW64__) \ || defined(__s390x__) \ - || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ - || defined(__EMSCRIPTEN__)) + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) ) #define FASTFLOAT_64BIT #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ || defined(__arm__) || defined(_M_ARM) \ - || defined(__MINGW32__)) + || defined(__MINGW32__) || defined(__EMSCRIPTEN__)) #define FASTFLOAT_32BIT #else // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. @@ -128,7 +127,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last, #define FASTFLOAT_VISUAL_STUDIO 1 #endif -#ifdef __BYTE_ORDER__ +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ #define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #elif defined _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 @@ -271,8 +270,9 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline value128 full_multiplication(uint64_t a, uint64_t b) { value128 answer; -#ifdef _M_ARM64 +#if defined(_M_ARM64) && !defined(__MINGW32__) // ARM64 has native support for 64-bit multiplications, no need to emulate + // But MinGW on ARM64 doesn't have native support for 64-bit multiplications answer.high = __umulh(a, b); answer.low = a * b; #elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) @@ -307,21 +307,69 @@ constexpr static double powers_of_ten_double[] = { 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10}; +// used for max_mantissa_double and max_mantissa_float +constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; +// Largest integer value v so that (5**index * v) <= 1<<53. +// 0x10000000000000 == 1 << 53 +constexpr static uint64_t max_mantissa_double[] = { + 0x10000000000000, + 0x10000000000000 / 5, + 0x10000000000000 / (5 * 5), + 0x10000000000000 / (5 * 5 * 5), + 0x10000000000000 / (5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555), + 0x10000000000000 / (constant_55555 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)}; + // Largest integer value v so that (5**index * v) <= 1<<24. + // 0x1000000 == 1<<24 + constexpr static uint64_t max_mantissa_float[] = { + 0x1000000, + 0x1000000 / 5, + 0x1000000 / (5 * 5), + 0x1000000 / (5 * 5 * 5), + 0x1000000 / (5 * 5 * 5 * 5), + 0x1000000 / (constant_55555), + 0x1000000 / (constant_55555 * 5), + 0x1000000 / (constant_55555 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * constant_55555), + 0x1000000 / (constant_55555 * constant_55555 * 5)}; template struct binary_format { + using equiv_uint = typename std::conditional::type; + static inline constexpr int mantissa_explicit_bits(); static inline constexpr int minimum_exponent(); static inline constexpr int infinite_power(); static inline constexpr int sign_index(); - static inline constexpr int min_exponent_fast_path(); static inline constexpr int max_exponent_fast_path(); static inline constexpr int max_exponent_round_to_even(); static inline constexpr int min_exponent_round_to_even(); - static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr uint64_t max_mantissa_fast_path(int64_t power); static inline constexpr int largest_power_of_ten(); static inline constexpr int smallest_power_of_ten(); static inline constexpr T exact_power_of_ten(int64_t power); static inline constexpr size_t max_digits(); + static inline constexpr equiv_uint exponent_mask(); + static inline constexpr equiv_uint mantissa_mask(); + static inline constexpr equiv_uint hidden_bit_mask(); }; template <> inline constexpr int binary_format::mantissa_explicit_bits() { @@ -364,21 +412,6 @@ template <> inline constexpr int binary_format::infinite_power() { template <> inline constexpr int binary_format::sign_index() { return 63; } template <> inline constexpr int binary_format::sign_index() { return 31; } -template <> inline constexpr int binary_format::min_exponent_fast_path() { -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - return 0; -#else - return -22; -#endif -} -template <> inline constexpr int binary_format::min_exponent_fast_path() { -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - return 0; -#else - return -10; -#endif -} - template <> inline constexpr int binary_format::max_exponent_fast_path() { return 22; } @@ -386,11 +419,17 @@ template <> inline constexpr int binary_format::max_exponent_fast_path() return 10; } -template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return uint64_t(2) << mantissa_explicit_bits(); +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 22 + // + return max_mantissa_double[power]; } -template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { - return uint64_t(2) << mantissa_explicit_bits(); +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 10 + // + return max_mantissa_float[power]; } template <> @@ -429,6 +468,33 @@ template <> inline constexpr size_t binary_format::max_digits() { return 114; } +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7F800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7FF0000000000000; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x007FFFFF; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x000FFFFFFFFFFFFF; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x00800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x0010000000000000; +} + template fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { uint64_t word = am.mantissa; @@ -2410,40 +2476,24 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n // this converts a native floating-point number to an extended-precision float. template fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + using equiv_uint = typename binary_format::equiv_uint; + constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); + constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); + constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); + adjusted_mantissa am; int32_t bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); - if (std::is_same::value) { - constexpr uint32_t exponent_mask = 0x7F800000; - constexpr uint32_t mantissa_mask = 0x007FFFFF; - constexpr uint64_t hidden_bit_mask = 0x00800000; - uint32_t bits; - ::memcpy(&bits, &value, sizeof(T)); - if ((bits & exponent_mask) == 0) { - // denormal - am.power2 = 1 - bias; - am.mantissa = bits & mantissa_mask; - } else { - // normal - am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); - am.power2 -= bias; - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; - } + equiv_uint bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; } else { - constexpr uint64_t exponent_mask = 0x7FF0000000000000; - constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; - constexpr uint64_t hidden_bit_mask = 0x0010000000000000; - uint64_t bits; - ::memcpy(&bits, &value, sizeof(T)); - if ((bits & exponent_mask) == 0) { - // denormal - am.power2 = 1 - bias; - am.mantissa = bits & mantissa_mask; - } else { - // normal - am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); - am.power2 -= bias; - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; - } + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; } return am; @@ -2869,11 +2919,10 @@ from_chars_result from_chars_advanced(const char *first, const char *last, } answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch; - // Next is Clinger's fast path. - if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path() && !pns.too_many_digits) { + // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal + if (pns.exponent >= 0 && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path(pns.exponent) && !pns.too_many_digits) { value = T(pns.mantissa); - if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } - else { value = value * binary_format::exact_power_of_ten(pns.exponent); } + value = value * binary_format::exact_power_of_ten(pns.exponent); if (pns.negative) { value = -value; } return answer; } diff --git a/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc b/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc new file mode 100644 index 0000000..95bf669 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc @@ -0,0 +1,42 @@ +// 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-do run { target c++17 } } +// { dg-add-options ieee } + +#include +#include +#include +#include + +int +main() +{ + // FP from_char not available otherwise. +#if __cpp_lib_to_chars >= 201611L \ + && _GLIBCXX_USE_C99_FENV_TR1 \ + && defined(FE_DOWNWARD) \ + && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) + // PR libstdc++/107468 + float f; + char buf[] = "3.355447e+07"; + std::fesetround(FE_DOWNWARD); + auto [ptr, ec] = std::from_chars(buf, buf + sizeof(buf) - 1, f, std::chars_format::scientific); + VERIFY( ec == std::errc() && ptr == buf + sizeof(buf) - 1 ); + VERIFY( f == 33554472.0f ); +#endif +} -- cgit v1.1 From f471cb71c86c1e7a3dead324142bdf880f00a3da Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 7 Nov 2022 13:29:30 -0500 Subject: libstdc++: Implement ranges::cartesian_product_view from P2374R4 This also implements the proposed resolutions of the tentatively ready LWG issues 3760, 3761 and 3801 for cartesian_product_view. I'm not sure how/if we should implement the recommended practice of: iterator::difference_type should be the smallest signed-integer-like type that is sufficiently wide to store the product of the maximum sizes of all underlying ranges if such a type exists because for e.g. extern std::vector x, y; auto v = views::cartesian_product(x, y); IIUC it'd mean difference_type should be __int128 (on 64-bit systems), which seems quite wasteful: in practice the size of any cartesian product probably won't exceed the precision of say ptrdiff_t, and using anything larger will just incur unnecessary space/time overhead. It's also probably not worth the complexity to use less precision than ptrdiff_t (when possible) either. So this patch defines difference_type as common_type_t, range_difference_t<_Vs>...> which should mean it's least as large as the difference_type of each underlying range, and at least as large as ptrdiff_t. This patch also adds assertions to catch any overflow that occurs due to this choice of difference_type. libstdc++-v3/ChangeLog: * include/std/ranges (__maybe_const_t): New alias for __detail::__maybe_const_t. (__detail::__cartesian_product_is_random_access): Define. (__detail::__cartesian_product_common_arg): Define. (__detail::__cartesian_product_is_bidirectional): Define. (__detail::__cartesian_product_is_common): Define. (__detail::__cartesian_product_is_sized): Define. (__detail::__cartesian_is_sized_sentinel): Define. (__detail::__cartesian_common_arg_end): Define. (cartesian_product_view): Define. (cartesian_product_view::_Iterator): Define. (views::__detail::__can_cartesian_product_view): Define. (views::_CartesianProduct, views::cartesian_product): Define. * testsuite/std/ranges/cartesian_product/1.cc: New test. --- libstdc++-v3/include/std/ranges | 513 +++++++++++++++++++++ .../testsuite/std/ranges/cartesian_product/1.cc | 186 ++++++++ 2 files changed, 699 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index a55e9e7..959886a 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -829,6 +829,9 @@ namespace __detail } // namespace __detail +// Shorthand for __detail::__maybe_const_t. +using __detail::__maybe_const_t; + namespace views::__adaptor { // True if the range adaptor _Adaptor can be applied with _Args. @@ -7973,6 +7976,516 @@ namespace views::__adaptor inline constexpr _Stride stride; } + + namespace __detail + { + template + concept __cartesian_product_is_random_access + = (random_access_range<__maybe_const_t<_Const, _First>> + && ... + && (random_access_range<__maybe_const_t<_Const, _Vs>> + && sized_range<__maybe_const_t<_Const, _Vs>>)); + + template + concept __cartesian_product_common_arg + = common_range<_Range> || (sized_range<_Range> && random_access_range<_Range>); + + template + concept __cartesian_product_is_bidirectional + = (bidirectional_range<__maybe_const_t<_Const, _First>> + && ... + && (bidirectional_range<__maybe_const_t<_Const, _Vs>> + && __cartesian_product_common_arg<__maybe_const_t<_Const, _Vs>>)); + + template + concept __cartesian_product_is_common = __cartesian_product_common_arg<_First>; + + template + concept __cartesian_product_is_sized = (sized_range<_Vs> && ...); + + template class FirstSent, typename _First, typename... _Vs> + concept __cartesian_is_sized_sentinel + = (sized_sentinel_for>, + iterator_t<__maybe_const_t<_Const, _First>>> + && ... + && (sized_range<__maybe_const_t<_Const, _Vs>> + && sized_sentinel_for>, + iterator_t<__maybe_const_t<_Const, _Vs>>>)); + + template<__cartesian_product_common_arg _Range> + constexpr auto + __cartesian_common_arg_end(_Range& __r) + { + if constexpr (common_range<_Range>) + return ranges::end(__r); + else + return ranges::begin(__r) + ranges::distance(__r); + } + } // namespace __detail + + template + requires (view<_First> && ... && view<_Vs>) + class cartesian_product_view : public view_interface> + { + tuple<_First, _Vs...> _M_bases; + + template class _Iterator; + + static auto + _S_difference_type() + { + // TODO: Implement the recommended practice of using the smallest + // sufficiently wide type according to the maximum sizes of the + // underlying ranges? + return common_type_t, + range_difference_t<_Vs>...>{}; + } + + public: + cartesian_product_view() = default; + + constexpr explicit + cartesian_product_view(_First __first, _Vs... __rest) + : _M_bases(std::move(__first), std::move(__rest)...) + { } + + constexpr _Iterator + begin() requires (!__detail::__simple_view<_First> || ... || !__detail::__simple_view<_Vs>) + { return _Iterator(*this, __detail::__tuple_transform(ranges::begin, _M_bases)); } + + constexpr _Iterator + begin() const requires (range && ... && range) + { return _Iterator(*this, __detail::__tuple_transform(ranges::begin, _M_bases)); } + + constexpr _Iterator + end() requires ((!__detail::__simple_view<_First> || ... || !__detail::__simple_view<_Vs>) + && __detail::__cartesian_product_is_common<_First, _Vs...>) + { + bool __empty_tail = [this](index_sequence<_Is...>) { + return (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); + }(make_index_sequence{}); + + auto __it = __detail::__tuple_transform(ranges::begin, _M_bases); + if (!__empty_tail) + std::get<0>(__it) = __detail::__cartesian_common_arg_end(std::get<0>(_M_bases)); + return _Iterator{*this, std::move(__it)}; + } + + constexpr _Iterator + end() const requires __detail::__cartesian_product_is_common + { + bool __empty_tail = [this](index_sequence<_Is...>) { + return (ranges::empty(std::get<1 + _Is>(_M_bases)) || ...); + }(make_index_sequence{}); + + auto __it = __detail::__tuple_transform(ranges::begin, _M_bases); + if (!__empty_tail) + std::get<0>(__it) = __detail::__cartesian_common_arg_end(std::get<0>(_M_bases)); + return _Iterator{*this, std::move(__it)}; + } + + constexpr default_sentinel_t + end() const noexcept + { return default_sentinel; } + + constexpr auto + size() requires __detail::__cartesian_product_is_sized<_First, _Vs...> + { + using _ST = __detail::__make_unsigned_like_t; + return [&](index_sequence<_Is...>) { + auto __size = static_cast<_ST>(1); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral<_ST>) + { + bool __overflow + = (__builtin_mul_overflow(__size, + static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))), + &__size) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __size = (static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))) * ...); + return __size; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + constexpr auto + size() const requires __detail::__cartesian_product_is_sized + { + using _ST = __detail::__make_unsigned_like_t; + return [&](index_sequence<_Is...>) { + auto __size = static_cast<_ST>(1); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral<_ST>) + { + bool __overflow + = (__builtin_mul_overflow(__size, + static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))), + &__size) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __size = (static_cast<_ST>(ranges::size(std::get<_Is>(_M_bases))) * ...); + return __size; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + }; + + template + cartesian_product_view(_Vs&&...) -> cartesian_product_view...>; + + template + requires (view<_First> && ... && view<_Vs>) + template + class cartesian_product_view<_First, _Vs...>::_Iterator + { + using _Parent = __maybe_const_t<_Const, cartesian_product_view>; + _Parent* _M_parent = nullptr; + __detail::__tuple_or_pair_t>, + iterator_t<__maybe_const_t<_Const, _Vs>>...> _M_current; + + constexpr + _Iterator(_Parent& __parent, decltype(_M_current) __current) + : _M_parent(std::__addressof(__parent)), + _M_current(std::move(__current)) + { } + + static auto + _S_iter_concept() + { + if constexpr (__detail::__cartesian_product_is_random_access<_Const, _First, _Vs...>) + return random_access_iterator_tag{}; + else if constexpr (__detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...>) + return bidirectional_iterator_tag{}; + else if constexpr (forward_range<__maybe_const_t<_Const, _First>>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + friend cartesian_product_view; + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = decltype(_S_iter_concept()); + using value_type + = __detail::__tuple_or_pair_t>, + range_value_t<__maybe_const_t<_Const, _Vs>>...>; + using reference + = __detail::__tuple_or_pair_t>, + range_reference_t<__maybe_const_t<_Const, _Vs>>...>; + using difference_type = decltype(cartesian_product_view::_S_difference_type()); + + _Iterator() requires forward_range<__maybe_const_t<_Const, _First>> = default; + + constexpr + _Iterator(_Iterator __i) + requires _Const + && (convertible_to, iterator_t> + && ... && convertible_to, iterator_t>) + : _M_parent(std::__addressof(__i._M_parent)), + _M_current(std::move(__i._M_current)) + { } + + constexpr auto + operator*() const + { + auto __f = [](auto& __i) -> decltype(auto) { + return *__i; + }; + return __detail::__tuple_transform(__f, _M_current); + } + + constexpr _Iterator& + operator++() + { + _M_next(); + return *this; + } + + constexpr void + operator++(int) + { ++*this; } + + constexpr _Iterator + operator++(int) requires forward_range<__maybe_const_t<_Const, _First>> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr _Iterator& + operator--() + requires __detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...> + { + _M_prev(); + return *this; + } + + constexpr _Iterator + operator--(int) + requires __detail::__cartesian_product_is_bidirectional<_Const, _First, _Vs...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { + _M_advance(__x); + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return *this += -__x; } + + constexpr reference + operator[](difference_type __n) const + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return *((*this) + __n); } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires equality_comparable>> + { return __x._M_current == __y._M_current; } + + friend constexpr bool + operator==(const _Iterator& __x, default_sentinel_t) + { + return [&](index_sequence<_Is...>) { + return ((std::get<_Is>(__x._M_current) + == ranges::end(std::get<_Is>(__x._M_parent->_M_bases))) + || ...); + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + friend constexpr auto + operator<=>(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _First, _Vs...> + { return __x._M_current <=> __y._M_current; } + + friend constexpr _Iterator + operator+(_Iterator __x, difference_type __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __x += __y; } + + friend constexpr _Iterator + operator+(difference_type __x, _Iterator __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __y += __x; } + + friend constexpr _Iterator + operator-(_Iterator __x, difference_type __y) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { return __x -= __y; } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires __detail::__cartesian_is_sized_sentinel<_Const, iterator_t, _First, _Vs...> + { return __x._M_distance_from(__y._M_current); } + + friend constexpr difference_type + operator-(const _Iterator& __i, default_sentinel_t) + requires __detail::__cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Vs...> + { + tuple __end_tuple = [&](index_sequence<_Is...>) { + return tuple{ranges::end(std::get<0>(__i._M_parent->_M_bases)), + ranges::begin(std::get<1 + _Is>(__i._M_parent->_M_bases))...}; + }(make_index_sequence{}); + return __i._M_distance_from(__end_tuple); + } + + friend constexpr difference_type + operator-(default_sentinel_t, const _Iterator& __i) + requires __detail::__cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Vs...> + { return -(__i - default_sentinel); } + + friend constexpr auto + iter_move(const _Iterator& __i) + { return __detail::__tuple_transform(ranges::iter_move, __i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __l, const _Iterator& __r) + requires (indirectly_swappable>> + && ... + && indirectly_swappable>>) + { + [&](index_sequence<_Is...>) { + (ranges::iter_swap(std::get<_Is>(__l._M_current), std::get<_Is>(__r._M_current)), ...); + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + private: + template + constexpr void + _M_next() + { + auto& __it = std::get<_Nm>(_M_current); + ++__it; + if constexpr (_Nm > 0) + if (__it == ranges::end(std::get<_Nm>(_M_parent->_M_bases))) + { + __it = ranges::begin(std::get<_Nm>(_M_parent->_M_bases)); + _M_next<_Nm - 1>(); + } + } + + template + constexpr void + _M_prev() + { + auto& __it = std::get<_Nm>(_M_current); + if (__it == ranges::begin(std::get<_Nm>(_M_parent->_M_bases))) + { + __it = __detail::__cartesian_common_arg_end(std::get<_Nm>(_M_parent->_M_bases)); + if constexpr (_Nm > 0) + _M_prev<_Nm - 1>(); + } + --__it; + } + + template + constexpr void + _M_advance(difference_type __x) + requires __detail::__cartesian_product_is_random_access<_Const, _First, _Vs...> + { + if (__x == 1) + _M_next<_Nm>(); + else if (__x == -1) + _M_prev<_Nm>(); + else if (__x != 0) + { + // Constant time iterator advancement. + auto& __r = std::get<_Nm>(_M_parent->_M_bases); + auto& __it = std::get<_Nm>(_M_current); + if constexpr (_Nm == 0) + { +#ifdef _GLIBCXX_ASSERTIONS + auto __size = ranges::ssize(__r); + auto __begin = ranges::begin(__r); + auto __offset = __it - __begin; + __glibcxx_assert(__offset + __x >= 0 && __offset + __x <= __size); +#endif + __it += __x; + } + else + { + auto __size = ranges::ssize(__r); + auto __begin = ranges::begin(__r); + auto __offset = __it - __begin; + __offset += __x; + __x = __offset / __size; + __offset %= __size; + if (__offset < 0) + { + __offset = __size + __offset; + --__x; + } + __it = __begin + __offset; + _M_advance<_Nm - 1>(__x); + } + } + } + + template + constexpr difference_type + _M_distance_from(const _Tuple& __t) const + { + return [&](index_sequence<_Is...>) { + auto __sum = static_cast(0); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow + = (__builtin_add_overflow(__sum, _M_scaled_distance<_Is>(__t), &__sum) + || ...); + __glibcxx_assert(!__overflow); + } + else +#endif + __sum = (_M_scaled_distance<_Is>(__t) + ...); + return __sum; + }(make_index_sequence<1 + sizeof...(_Vs)>{}); + } + + template + constexpr difference_type + _M_scaled_distance(const _Tuple& __t) const + { + auto __dist = static_cast(std::get<_Nm>(_M_current) + - std::get<_Nm>(__t)); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow = __builtin_mul_overflow(__dist, _M_scaled_size<_Nm+1>(), &__dist); + __glibcxx_assert(!__overflow); + } + else +#endif + __dist *= _M_scaled_size<_Nm+1>(); + return __dist; + } + + template + constexpr difference_type + _M_scaled_size() const + { + if constexpr (_Nm <= sizeof...(_Vs)) + { + auto __size = static_cast(ranges::size + (std::get<_Nm>(_M_parent->_M_bases))); +#ifdef _GLIBCXX_ASSERTIONS + if constexpr (integral) + { + bool __overflow = __builtin_mul_overflow(__size, _M_scaled_size<_Nm+1>(), &__size); + __glibcxx_assert(!__overflow); + } + else +#endif + __size *= _M_scaled_size<_Nm+1>(); + return __size; + } + else + return static_cast(1); + } + }; + + namespace views + { + namespace __detail + { + template + concept __can_cartesian_product_view + = requires { cartesian_product_view...>(std::declval<_Ts>()...); }; + } + + struct _CartesianProduct + { + template + requires (sizeof...(_Ts) == 0 || __detail::__can_cartesian_product_view<_Ts...>) + constexpr auto + operator() [[nodiscard]] (_Ts&&... __ts) const + { + if constexpr (sizeof...(_Ts) == 0) + return views::empty>; + else + return cartesian_product_view...>(std::forward<_Ts>(__ts)...); + } + }; + + inline constexpr _CartesianProduct cartesian_product; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc b/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc new file mode 100644 index 0000000..d6e4b53 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/cartesian_product/1.cc @@ -0,0 +1,186 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + int x[] = {1, 2, 3}; + int y[] = {4, 5, 6}; + int z[] = {7, 8}; + int w[] = {9}; + + auto v0 = views::cartesian_product(); + VERIFY( ranges::end(v0) - ranges::begin(v0) == 0 ); + VERIFY( ranges::size(v0) == 0 ); + VERIFY( ranges::empty(v0) ); + + auto v1 = views::cartesian_product(x); + VERIFY( ranges::end(v1) - ranges::begin(v1) == 3 ); + VERIFY( ranges::size(v1) == 3 ); + VERIFY( ranges::equal(v1 | views::keys, x) ); + VERIFY( std::get<0>(v1[0]) == 1 ); + VERIFY( std::get<0>(v1[1]) == 2 ); + VERIFY( std::get<0>(v1[2]) == 3 ); + VERIFY( ranges::equal(v1 | views::reverse | views::keys, x | views::reverse)); + + auto v2 = views::cartesian_product(x, y); + VERIFY( ranges::size(v2) == 9 ); + VERIFY( ranges::end(v2) - ranges::begin(v2) == 9 ); + VERIFY( ranges::equal(v2 | views::keys, (int[]){1, 1, 1, 2, 2, 2, 3, 3, 3})); + VERIFY( ranges::equal(v2 | views::values, (int[]){4, 5, 6, 4, 5, 6, 4, 5, 6})); + VERIFY( ranges::equal(v2 | views::reverse | views::keys, (int[]){3, 3, 3, 2, 2, 2, 1, 1, 1}) ); + VERIFY( ranges::equal(v2 | views::reverse | views::values, (int[]){6, 5, 4, 6, 5, 4, 6, 5, 4}) ); + + auto v3 = views::cartesian_product(x, y, z); + VERIFY( ranges::size(v3) == 18 ); + VERIFY( ranges::equal(v3, (std::tuple[]){{1,4,7}, {1,4,8}, {1,5,7}, {1,5,8}, + {1,6,7}, {1,6,8}, {2,4,7}, {2,4,8}, + {2,5,7}, {2,5,8}, {2,6,7}, {2,6,8}, + {3,4,7}, {3,4,8}, {3,5,7}, {3,5,8}, + {3,6,7}, {3,6,8}}) ); + + auto v4 = views::cartesian_product(x, y, z, w); + VERIFY( ranges::size(v4) == 18 ); + VERIFY( ranges::equal(v4 | views::elements<3>, views::repeat(9, 18)) ); + + auto i4 = v4.begin(), j4 = i4 + 1; + VERIFY( j4 > i4 ); + VERIFY( i4[0] == std::tuple(1, 4, 7, 9) ); + VERIFY( i4 + 18 == v4.end() ); + i4 += 5; + VERIFY( i4 != v4.begin() ); + VERIFY( i4 - 5 == v4.begin() ); + VERIFY( *i4 == std::tuple(1, 6, 8, 9) ); + VERIFY( i4 - 5 != i4 ); + i4 -= 3; + VERIFY( *i4 == std::tuple(1, 5, 7, 9) ); + VERIFY( j4 + 1 == i4 ); + ranges::iter_swap(i4, j4); + VERIFY( *j4 == std::tuple(1, 5, 7, 9) ); + VERIFY( *i4 == std::tuple(1, 4, 8, 9) ); + + return true; +} + +void +test02() +{ + int x[] = {1, 2}; + __gnu_test::test_input_range rx(x); + auto v = views::cartesian_product(rx, x); + auto i = v.begin(); + std::default_sentinel_t s = v.end(); + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 1 && std::get<1>(*i) == 1 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 1 && std::get<1>(*i) == 2 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 2 && std::get<1>(*i) == 1 ); + ++i; + VERIFY( i != s ); + VERIFY( std::get<0>(*i) == 2 && std::get<1>(*i) == 2 ); + ++i; + VERIFY( i == s ); +} + +void +test03() +{ + int x[2]; + __gnu_test::test_input_range rx(x); + auto v = views::cartesian_product(views::counted(rx.begin(), 2), x); + VERIFY( v.size() == 4 ); + auto i = v.begin(); + std::default_sentinel_t s = v.end(); + VERIFY( i - s == -4 ); + VERIFY( s - i == 4 ); + ++i; + VERIFY( i - s == -3 ); + VERIFY( s - i == 3 ); + ++i; + VERIFY( i - s == -2 ); + VERIFY( s - i == 2 ); + ++i; + VERIFY( i - s == -1 ); + VERIFY( s - i == 1 ); + ++i; + VERIFY( i - s == 0 ); + VERIFY( s - i == 0 ); +} + +void +test04() +{ + // Exhaustively verify correctness of our iterator addition implementation + // (which runs in constant time) for this 24-element cartesian_product_view. + int x[4], y[3], z[2], w[1]; + auto v = views::cartesian_product(x, y, z, w); + + auto n = ranges::ssize(v); + for (int i = 0; i <= n; i++) + for (int j = 0; i + j <= n; j++) + { + auto b1 = v.begin(); + for (int k = 0; k < i + j; k++) + ++b1; + VERIFY( b1 - v.begin() == i + j ); + auto b2 = (v.begin() + i) + j; + auto b3 = v.begin() + (i + j); + VERIFY( b1 == b2 && b2 == b3 ); + + auto e1 = v.end(); + for (int k = 0; k < i + j; k++) + --e1; + VERIFY( v.end() - e1 == i + j ); + auto e2 = (v.end() - i) - j; + auto e3 = v.end() - (i + j); + VERIFY( e1 == e2 && e2 == e3 ); + } +} + +void +test05() +{ +#if __SIZEOF_INT128__ + auto r = views::iota(__int128(0), __int128(5)); +#else + auto r = views::iota(0ll, 5ll); +#endif + auto v = views::cartesian_product(r, r); + VERIFY( ranges::size(v) == 25 ); + VERIFY( v.end() - v.begin() == 25 ); + VERIFY( v.begin() + ranges::ssize(v) - v.begin() == 25 ); +} + +constexpr bool +test06() +{ + int x[] = {1, 2, 3}; + auto v = views::cartesian_product(x, views::empty, x); + VERIFY( ranges::size(v) == 0 ); + VERIFY( ranges::begin(v) == ranges::end(v) ); + VERIFY( ranges::begin(v) - ranges::begin(v) == 0 ); + + return true; +} + +int +main() +{ + static_assert(test01()); + test02(); + test03(); + test04(); + test05(); + static_assert(test06()); +} -- cgit v1.1 From 2ee0165f72be96083deaa8fd315bcfed011acd52 Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Mon, 7 Nov 2022 13:29:42 -0500 Subject: libstdc++: Implement ranges::as_rvalue_view from P2446R2 libstdc++-v3/ChangeLog: * include/std/ranges (as_rvalue_view): Define. (enable_borrowed_range): Define. (views::__detail::__can_as_rvalue_view): Define. (views::_AsRvalue, views::as_rvalue): Define. * testsuite/std/ranges/adaptors/as_rvalue/1.cc: New test. --- libstdc++-v3/include/std/ranges | 90 ++++++++++++++++++++++ .../testsuite/std/ranges/adaptors/as_rvalue/1.cc | 47 +++++++++++ 2 files changed, 137 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 959886a..ba544e1 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -8486,6 +8486,96 @@ namespace views::__adaptor inline constexpr _CartesianProduct cartesian_product; } + + template + requires view<_Vp> + class as_rvalue_view : public view_interface> + { + _Vp _M_base = _Vp(); + + public: + as_rvalue_view() requires default_initializable<_Vp> = default; + + constexpr explicit + as_rvalue_view(_Vp __base) + : _M_base(std::move(__base)) + { } + + constexpr _Vp + base() const& requires copy_constructible<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr auto + begin() requires (!__detail::__simple_view<_Vp>) + { return move_iterator(ranges::begin(_M_base)); } + + constexpr auto + begin() const requires range + { return move_iterator(ranges::begin(_M_base)); } + + constexpr auto + end() requires (!__detail::__simple_view<_Vp>) + { + if constexpr (common_range<_Vp>) + return move_iterator(ranges::end(_M_base)); + else + return move_sentinel(ranges::end(_M_base)); + } + + constexpr auto + end() const requires range + { + if constexpr (common_range) + return move_iterator(ranges::end(_M_base)); + else + return move_sentinel(ranges::end(_M_base)); + } + + constexpr auto + size() requires sized_range<_Vp> + { return ranges::size(_M_base); } + + constexpr auto + size() const requires sized_range + { return ranges::size(_M_base); } + }; + + template + as_rvalue_view(_Range&&) -> as_rvalue_view>; + + template + inline constexpr bool enable_borrowed_range> + = enable_borrowed_range<_Tp>; + + namespace views + { + namespace __detail + { + template + concept __can_as_rvalue_view = requires { as_rvalue_view(std::declval<_Tp>()); }; + } + + struct _AsRvalue : __adaptor::_RangeAdaptorClosure + { + template + requires __detail::__can_as_rvalue_view<_Range> + constexpr auto + operator() [[nodiscard]] (_Range&& __r) const + { + if constexpr (same_as, + range_reference_t<_Range>>) + return views::all(std::forward<_Range>(__r)); + else + return as_rvalue_view(std::forward<_Range>(__r)); + } + }; + + inline constexpr _AsRvalue as_rvalue; + } #endif // C++23 } // namespace ranges diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc new file mode 100644 index 0000000..8ca4f50 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/as_rvalue/1.cc @@ -0,0 +1,47 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include +#include +#include +#include +#include + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + + std::unique_ptr a[3] = { std::make_unique(1), + std::make_unique(2), + std::make_unique(3) }; + std::unique_ptr b[3]; + auto v = a | views::as_rvalue; + ranges::copy(v, b); + VERIFY( ranges::all_of(a, [](auto& p) { return p.get() == nullptr; }) ); + VERIFY( ranges::equal(b | views::transform([](auto& p) { return *p; }), (int[]){1, 2, 3}) ); + + return true; +} + +void +test02() +{ + std::unique_ptr x = std::make_unique(42); + std::unique_ptr y; + __gnu_test::test_input_range rx(&x, &x+1); + auto v = rx | views::as_rvalue; + static_assert(!ranges::common_range); + ranges::copy(v, &y); + VERIFY( x.get() == nullptr ); + VERIFY( *y == 42 ); +} + +int +main() +{ + static_assert(test01()); + test02(); +} -- cgit v1.1 From b457b779427b0f7b3fbac447811c9c52db5bc79e Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 8 Nov 2022 00:35:09 +0100 Subject: libstdc++: Fix up libstdc++ build against glibc 2.25 or older [PR107562] On Mon, Nov 07, 2022 at 05:48:42PM +0000, Jonathan Wakely wrote: > On Mon, 7 Nov 2022 at 16:11, Joseph Myers wrote: > > > > On Wed, 2 Nov 2022, Jakub Jelinek via Gcc-patches wrote: > > > > > 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 > > > > This support for older glibc doesn't actually seem to be working, on an > > older system with glibc 2.19 I'm seeing > > > > /scratch/jmyers/fsf/gcc-mainline/libstdc++-v3/src/c++17/floating_to_chars.cc:52:3: error: expected initializer before '__asm' > > 52 | __asm ("strfromf128"); > > | ^~~~~ > > > > and a series of subsequent errors. > > This seems to "fix" it (not sure if it's right though): > > #ifndef _GLIBCXX_HAVE_FLOAT128_MATH > extern "C" _Float128 __strtof128(const char*, char**) > __attribute__((__weak__)); > #endif > extern "C" _Float128 __strtof128(const char*, char**) > __asm ("strtof128"); It is, but floating_from_chars.cc has the same problem, and I think we can avoid the duplication, like this: 2022-11-08 Jakub Jelinek PR libstdc++/107562 * src/c++17/floating_from_chars.cc (__strtof128): Put __asm before __attribute__. * src/c++17/floating_to_chars.cc (__strfromf128): Likewise. --- libstdc++-v3/src/c++17/floating_from_chars.cc | 3 ++- libstdc++-v3/src/c++17/floating_to_chars.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 11a9be6..29eb463 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -63,10 +63,11 @@ extern "C" __ieee128 __strtoieee128(const char*, char**); && defined(__GLIBC_PREREQ) #define USE_STRTOF128_FOR_FROM_CHARS 1 extern "C" _Float128 __strtof128(const char*, char**) + __asm ("strtof128") #ifndef _GLIBCXX_HAVE_FLOAT128_MATH __attribute__((__weak__)) #endif - __asm ("strtof128"); + ; #endif #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \ diff --git a/libstdc++-v3/src/c++17/floating_to_chars.cc b/libstdc++-v3/src/c++17/floating_to_chars.cc index d6be646..afce8d7 100644 --- a/libstdc++-v3/src/c++17/floating_to_chars.cc +++ b/libstdc++-v3/src/c++17/floating_to_chars.cc @@ -46,10 +46,11 @@ 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) + __asm ("strfromf128") #ifndef _GLIBCXX_HAVE_FLOAT128_MATH __attribute__((__weak__)) #endif - __asm ("strfromf128"); + ; #endif // This implementation crucially assumes float/double have the -- cgit v1.1 From f8d901d00e94e5a03c3321b37303eddd7c321ecb Mon Sep 17 00:00:00 2001 From: GCC Administrator Date: Tue, 8 Nov 2022 00:17:53 +0000 Subject: Daily bump. --- libstdc++-v3/ChangeLog | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index fe27057..5874c62 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,73 @@ +2022-11-07 Jakub Jelinek + + PR libstdc++/107562 + * src/c++17/floating_from_chars.cc (__strtof128): Put __asm before + __attribute__. + * src/c++17/floating_to_chars.cc (__strfromf128): Likewise. + +2022-11-07 Patrick Palka + + * include/std/ranges (as_rvalue_view): Define. + (enable_borrowed_range): Define. + (views::__detail::__can_as_rvalue_view): Define. + (views::_AsRvalue, views::as_rvalue): Define. + * testsuite/std/ranges/adaptors/as_rvalue/1.cc: New test. + +2022-11-07 Patrick Palka + + * include/std/ranges (__maybe_const_t): New alias for + __detail::__maybe_const_t. + (__detail::__cartesian_product_is_random_access): Define. + (__detail::__cartesian_product_common_arg): Define. + (__detail::__cartesian_product_is_bidirectional): Define. + (__detail::__cartesian_product_is_common): Define. + (__detail::__cartesian_product_is_sized): Define. + (__detail::__cartesian_is_sized_sentinel): Define. + (__detail::__cartesian_common_arg_end): Define. + (cartesian_product_view): Define. + (cartesian_product_view::_Iterator): Define. + (views::__detail::__can_cartesian_product_view): Define. + (views::_CartesianProduct, views::cartesian_product): Define. + * testsuite/std/ranges/cartesian_product/1.cc: New test. + +2022-11-07 Jakub Jelinek + + PR libstdc++/107468 + * src/c++17/fast_float/MERGE: Adjust for merge from upstream. + * src/c++17/fast_float/LOCAL_PATCHES: Remove commits that were + upstreamed. + * src/c++17/fast_float/README.md: Merge from fast_float + 662497742fea7055f0e0ee27e5a7ddc382c2c38e commit. + * src/c++17/fast_float/fast_float.h: Likewise. + * testsuite/20_util/from_chars/pr107468.cc: New test. + +2022-11-07 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. + 2022-11-06 Patrick Palka * include/bits/atomic_wait.h (_detail::__platform_wait_alignment): -- cgit v1.1 From 431be04b8b6e31d950ddab340ed866d197d23d4d Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 4 Nov 2022 15:22:45 -0400 Subject: c++: implement P2468R2, the equality operator you are looking for This paper is resolving the problem of well-formed C++17 code becoming ambiguous in C++20 due to asymmetrical operator== being compared with itself in reverse. I had previously implemented a tiebreaker such that if the two candidates were functions with the same parameter types, we would prefer the non-reversed candidate. But the committee went with a different approach: if there's an operator!= with the same parameter types as the operator==, don't consider the reversed form of the ==. So this patch implements that, and changes my old tiebreaker to give a pedwarn if it is used. I also noticed that we were giving duplicate errors for some testcases, and fixed the tourney logic to avoid that. As a result, a lot of tests of the form struct A { bool operator==(const A&); }; need to be fixed to add a const function-cv-qualifier, e.g. struct A { bool operator==(const A&) const; }; The committee thought such code ought to be fixed, so breaking it was fine. 18_support/comparisons/algorithms/fallback.cc also breaks with this patch, because of the similarly asymmetrical bool operator==(const S&, S&) { return true; } As a result, some of the asserts need to be reversed. The H test in spaceship-eq15.C is specified in the standard to be well-formed because the op!= in the inline namespace is not found by the search, but that seems wrong to me. I've implemented that behavior, but disabled it for now; if we decide that is the way we want to go, we can just remove the "0 &&" in add_candidates to enable it. Co-authored-by: Jakub Jelinek gcc/cp/ChangeLog: * cp-tree.h (fns_correspond): Declare. * decl.cc (fns_correspond): New. * call.cc (add_candidates): Look for op!= matching op==. (joust): Complain about non-standard reversed tiebreaker. (tourney): Fix champ_compared_to_predecessor logic. (build_new_op): Don't complain about error_mark_node not having 'bool' type. * pt.cc (tsubst_copy_and_build): Don't try to be permissive when seen_error(). gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-eq15.C: New test. * g++.dg/cpp0x/defaulted3.C: Add const. * g++.dg/cpp2a/bit-cast7.C: Add const. * g++.dg/cpp2a/spaceship-rewrite1.C: Expect error. * g++.dg/cpp2a/spaceship-rewrite5.C: Expect error. * g++.old-deja/g++.jason/byval2.C: Expect error. * g++.old-deja/g++.other/overload13.C: Add const. libstdc++-v3/ChangeLog: * testsuite/18_support/comparisons/algorithms/fallback.cc: Adjust asserts. --- .../testsuite/18_support/comparisons/algorithms/fallback.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc b/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc index 05e1bf7..8bf78fa 100644 --- a/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc +++ b/libstdc++-v3/testsuite/18_support/comparisons/algorithms/fallback.cc @@ -31,12 +31,12 @@ template using adl::S; -static_assert( has_strong_order_fallback ); +static_assert( ! has_strong_order_fallback ); static_assert( has_strong_order_fallback ); static_assert( ! has_strong_order_fallback ); -static_assert( has_weak_order_fallback ); +static_assert( ! has_weak_order_fallback ); static_assert( has_weak_order_fallback ); static_assert( ! has_weak_order_fallback ); -static_assert( has_partial_order_fallback ); +static_assert( ! has_partial_order_fallback ); static_assert( ! has_partial_order_fallback ); // LWG 3465 static_assert( ! has_partial_order_fallback ); -- cgit v1.1 From 8d0326943ee4eb87309faca28ee0ed13346dd70a Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Tue, 8 Nov 2022 01:39:52 +0000 Subject: libstdc++: Fix syntax error in old-glibc case in floating_from_chars.cc [PR107562] PR libstdc++/107562 * src/c++17/floating_from_chars.cc (from_chars_impl): Fix syntax error. --- libstdc++-v3/src/c++17/floating_from_chars.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc index 29eb463..be1e105 100644 --- a/libstdc++-v3/src/c++17/floating_from_chars.cc +++ b/libstdc++-v3/src/c++17/floating_from_chars.cc @@ -632,7 +632,7 @@ namespace { #ifndef _GLIBCXX_HAVE_FLOAT128_MATH if (&__strtof128 == nullptr) - tmpval = _Float128(std::strtold(str, &endptr); + tmpval = _Float128(std::strtold(str, &endptr)); else #endif tmpval = __strtof128(str, &endptr); -- cgit v1.1 From 4596339d9fabdcbd66b5a7430fa56544f75ecef1 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 7 Nov 2022 22:27:08 +0000 Subject: libstdc++: Remove empty elements in manual This fixes a spurious comma before the list of authors in the PDF version of the libstdc++ manual. Also fix the commented-out examples which should show not . libstdc++-v3/ChangeLog: * doc/xml/authors.xml: Remove empty author element. * doc/xml/manual/spine.xml: Likewise. * doc/html/manual/index.html: Regenerate. --- libstdc++-v3/doc/html/manual/index.html | 2 +- libstdc++-v3/doc/xml/authors.xml | 9 ++------- libstdc++-v3/doc/xml/manual/spine.xml | 9 ++------- 3 files changed, 5 insertions(+), 15 deletions(-) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/doc/html/manual/index.html b/libstdc++-v3/doc/html/manual/index.html index 292afde..e544152 100644 --- a/libstdc++-v3/doc/html/manual/index.html +++ b/libstdc++-v3/doc/html/manual/index.html @@ -1,5 +1,5 @@ -The GNU C++ Library Manual

The GNU C++ Library Manual

Paolo Carlini

Phil Edwards

Doug Gregor

Benjamin Kosnik

Dhruv Matani

Jason Merrill

Mark Mitchell

Nathan Myers

Felix Natter

Stefan Olsson

Johannes Singler

Ami Tavory

Jonathan Wakely

The GNU C++ Library Manual

Paolo Carlini

Phil Edwards

Doug Gregor

Benjamin Kosnik

Dhruv Matani

Jason Merrill

Mark Mitchell

Nathan Myers

Felix Natter

Stefan Olsson

Johannes Singler

Ami Tavory

Jonathan Wakely


Table of Contents

I. Introduction diff --git a/libstdc++-v3/doc/xml/authors.xml b/libstdc++-v3/doc/xml/authors.xml index 0a120b5..95626d4 100644 --- a/libstdc++-v3/doc/xml/authors.xml +++ b/libstdc++-v3/doc/xml/authors.xml @@ -13,18 +13,13 @@ - + - + --> - - - - - PaoloCarlini TR1, LWG Active, Closed, Defects lists. diff --git a/libstdc++-v3/doc/xml/manual/spine.xml b/libstdc++-v3/doc/xml/manual/spine.xml index 34daa33..eff72bc 100644 --- a/libstdc++-v3/doc/xml/manual/spine.xml +++ b/libstdc++-v3/doc/xml/manual/spine.xml @@ -48,18 +48,13 @@ - + - + --> - - - - - PaoloCarlini TR1, LWG Active, Closed, Defects lists. -- cgit v1.1 From acbfa2bc6006dacda86e3fa53d545e89b66a9533 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 7 Nov 2022 22:52:31 +0000 Subject: libstdc++: Update my author blurb in the manual libstdc++-v3/ChangeLog: * doc/xml/authors.xml: Update the blurb listing my doc contributions. --- libstdc++-v3/doc/xml/authors.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'libstdc++-v3') diff --git a/libstdc++-v3/doc/xml/authors.xml b/libstdc++-v3/doc/xml/authors.xml index 95626d4..ee8e835 100644 --- a/libstdc++-v3/doc/xml/authors.xml +++ b/libstdc++-v3/doc/xml/authors.xml @@ -102,7 +102,10 @@ JonathanWakely - shared_ptr, markup editing and styling + Allocators, ABI History, API Evolution and Deprecation History, + Experimental Library Extensions, Implementation Status, + Running the Testsuite, Shared Library Versioning, Using Macros, + Using Exceptions, shared_ptr, markup editing and styling. -- cgit v1.1 From ee86bdd1d367bc174d7b50bd2ffa5622c4766322 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 8 Nov 2022 11:19:25 +0100 Subject: libstdc++: Uncomment denorm_min test As r13-3609-g6d9dbdf51f9afe8 has been committed, we can now enable even the denorm_min test. 2022-11-08 Jakub Jelinek * testsuite/20_util/to_chars/float128_c++23.cc (test): Uncomment denorm_min test. --- libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libstdc++-v3') 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 index 4c01458..28824c9 100644 --- a/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc +++ b/libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc @@ -34,7 +34,7 @@ void test(std::chars_format fmt = std::chars_format{}) { std::float128_t tests[] = { -// std::numeric_limits::denorm_min(), + std::numeric_limits::denorm_min(), std::numeric_limits::min(), 0.0f128, -42.0f128, -- cgit v1.1