On Mon, 7 Nov 2022 at 08:19, Jakub Jelinek <ja...@redhat.com> wrote: > > Hi! > > 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). > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK, thanks. > > 2022-11-07 Jakub Jelinek <ja...@redhat.com> > > 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/MERGE.jj 2022-01-18 11:59:00.306971713 > +0100 > +++ libstdc++-v3/src/c++17/fast_float/MERGE 2022-11-05 18:42:50.815892080 > +0100 > @@ -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. > --- libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES.jj 2022-02-04 > 14:36:56.965577924 +0100 > +++ libstdc++-v3/src/c++17/fast_float/LOCAL_PATCHES 2022-11-05 > 19:02:57.360336939 +0100 > @@ -1,4 +1,2 @@ > r12-6647 > r12-6648 > -r12-6664 > -r12-6665 > --- libstdc++-v3/src/c++17/fast_float/README.md.jj 2022-01-18 > 11:59:00.306971713 +0100 > +++ libstdc++-v3/src/c++17/fast_float/README.md 2022-11-05 18:32:34.668345927 > +0100 > @@ -1,12 +1,5 @@ > ## fast_float number parsing library: 4x faster than strtod > > -/badge.svg) > -/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 <iostream> > - > + > 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 restr > > 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 <iostream> > - > + > 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 numbe > <img > src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" > width="400"> > > ``` > -$ ./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 > > ## 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 > --- libstdc++-v3/src/c++17/fast_float/fast_float.h.jj 2022-02-04 > 14:36:56.966577910 +0100 > +++ libstdc++-v3/src/c++17/fast_float/fast_float.h 2022-11-05 > 18:54:48.096049177 +0100 > @@ -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<typename T> > @@ -98,12 +98,11 @@ from_chars_result from_chars_advanced(co > || 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(co > #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 _umul12 > 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_do > 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 <typename T> struct binary_format { > + using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, > uint64_t>::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<double>::mantissa_explicit_bits() { > @@ -364,21 +412,6 @@ template <> inline constexpr int binary_ > template <> inline constexpr int binary_format<double>::sign_index() { > return 63; } > template <> inline constexpr int binary_format<float>::sign_index() { return > 31; } > > -template <> inline constexpr int > binary_format<double>::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<float>::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<double>::max_exponent_fast_path() { > return 22; > } > @@ -386,11 +419,17 @@ template <> inline constexpr int binary_ > return 10; > } > > -template <> inline constexpr uint64_t > binary_format<double>::max_mantissa_fast_path() { > - return uint64_t(2) << mantissa_explicit_bits(); > +template <> inline constexpr uint64_t > binary_format<double>::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<float>::max_mantissa_fast_path() { > - return uint64_t(2) << mantissa_explicit_bits(); > +template <> inline constexpr uint64_t > binary_format<float>::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 bina > return 114; > } > > +template <> inline constexpr binary_format<float>::equiv_uint > + binary_format<float>::exponent_mask() { > + return 0x7F800000; > +} > +template <> inline constexpr binary_format<double>::equiv_uint > + binary_format<double>::exponent_mask() { > + return 0x7FF0000000000000; > +} > + > +template <> inline constexpr binary_format<float>::equiv_uint > + binary_format<float>::mantissa_mask() { > + return 0x007FFFFF; > +} > +template <> inline constexpr binary_format<double>::equiv_uint > + binary_format<double>::mantissa_mask() { > + return 0x000FFFFFFFFFFFFF; > +} > + > +template <> inline constexpr binary_format<float>::equiv_uint > + binary_format<float>::hidden_bit_mask() { > + return 0x00800000; > +} > +template <> inline constexpr binary_format<double>::equiv_uint > + binary_format<double>::hidden_bit_mask() { > + return 0x0010000000000000; > +} > + > template<typename T> > 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 scientif > // this converts a native floating-point number to an extended-precision > float. > template <typename T> > fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { > + using equiv_uint = typename binary_format<T>::equiv_uint; > + constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); > + constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); > + constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); > + > adjusted_mantissa am; > int32_t bias = binary_format<T>::mantissa_explicit_bits() - > binary_format<T>::minimum_exponent(); > - if (std::is_same<T, float>::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<T>::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<T>::mantissa_explicit_bits()); > - am.power2 -= bias; > - am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; > - } > + // normal > + am.power2 = int32_t((bits & exponent_mask) >> > binary_format<T>::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(co > } > answer.ec = std::errc(); // be optimistic > answer.ptr = pns.lastmatch; > - // Next is Clinger's fast path. > - if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && > pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa > <=binary_format<T>::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<T>::max_exponent_fast_path() && pns.mantissa > <=binary_format<T>::max_mantissa_fast_path(pns.exponent) && > !pns.too_many_digits) { > value = T(pns.mantissa); > - if (pns.exponent < 0) { value = value / > binary_format<T>::exact_power_of_ten(-pns.exponent); } > - else { value = value * > binary_format<T>::exact_power_of_ten(pns.exponent); } > + value = value * binary_format<T>::exact_power_of_ten(pns.exponent); > if (pns.negative) { value = -value; } > return answer; > } > --- libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc.jj 2022-11-05 > 19:20:28.944898668 +0100 > +++ libstdc++-v3/testsuite/20_util/from_chars/pr107468.cc 2022-11-05 > 19:26:44.318740322 +0100 > @@ -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 > +// <http://www.gnu.org/licenses/>. > + > +// { dg-do run { target c++17 } } > +// { dg-add-options ieee } > + > +#include <charconv> > +#include <string> > +#include <cfenv> > +#include <testsuite_hooks.h> > + > +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 > +} > > Jakub >