public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>,
	gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH] libstdc++: Update from latest fast_float [PR107468]
Date: Mon, 7 Nov 2022 13:37:25 +0000	[thread overview]
Message-ID: <CACb0b4mVmHVLscotbq3iyDZQnJLOyxLfWS0yAjU5p2khe7QPnA@mail.gmail.com> (raw)
In-Reply-To: <Y2i/dVyDDbzKHeoO@tucnak>

On Mon, 7 Nov 2022 at 08:19, Jakub Jelinek <jakub@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  <jakub@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
>
> -![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 <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
>


      reply	other threads:[~2022-11-07 13:37 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-07  8:19 Jakub Jelinek
2022-11-07 13:37 ` Jonathan Wakely [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACb0b4mVmHVLscotbq3iyDZQnJLOyxLfWS0yAjU5p2khe7QPnA@mail.gmail.com \
    --to=jwakely@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=libstdc++@gcc.gnu.org \
    --cc=ppalka@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).