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++: Add _Float128 to_chars/from_chars support for x86, ia64 and ppc64le with glibc
Date: Mon, 7 Nov 2022 13:45:04 +0000	[thread overview]
Message-ID: <CACb0b4nNR5vqBe_H5nWv+JFhzveD8Jjypyrqruse-ko5yUn3Yw@mail.gmail.com> (raw)
In-Reply-To: <Y2I3pr1Eyn120h1C@tucnak>

On Wed, 2 Nov 2022 at 09:26, Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> The following patch adds std::{to,from}_chars support for std::float128_t
> on glibc 2.26+ for {i?86,x86_64,ia64,powerpc64le}-linux.
> When long double is already IEEE quad, previous changes already handle
> it by using long double overloads in _Float128 overloads.
> The powerpc64le case (with explicit or implicit -mabi=ibmlongdouble)
> is handled by using the __float128/__ieee128 entrypoints which are
> already in the library and used for -mabi=ieeelongdouble.
> For i?86, x86_64 and ia64 this patch adds new library entrypoints,
> mostly by enabling the code that was already there for powerpc64le-linux.
> Those use __float128 or __ieee128, the patch uses _Float128 for the
> exported overloads and internally as template parameter.  While
> powerpc64le-linux uses __sprintfieee128 and __strtoieee128,
> for _Float128 the patch uses the glibc 2.26 strfromf128 and strtof128
> APIs.  So that one can build gcc against older glibc and then compile
> user programs on newer glibc, the patch uses weak references unless
> gcc is compiled against glibc 2.26+.  strfromf128 unfortunately can't
> handle %.0Lf and %.*Le, %.*Lf, %.*Lg format strings sprintf/__sprintfieee128
> use, we need to remove the L from those and replace * with actually
> directly printing the precision into the format string (i.e. it can
> handle %.0f and %.27f (floating point type is implied from the function
> name)).

Wow, nice work juggling all the pieces here.

> Unlike the std::{,b}float16_t support, this one actually exports APIs
> with std::float128_t aka _Float128 in the mangled name, because no
> standard format is superset of it.  On the other side, e.g. on i?86/x86_64
> it doesn't have restrictions like for _Float16/__bf16 which ISAs need
> to be enabled in order to use it.
>
> The denorm_min case in the testcase is temporarily commented out because
> of the ERANGE subnormal issue Patrick posted patch for.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK, thanks!


>
> 2022-11-02  Jakub Jelinek  <jakub@redhat.com>
>
>         * include/std/charconv (from_chars, to_chars): Add _Float128
>         overfloads if _GLIBCXX_HAVE_FLOAT128_MATH is defined.
>         * config/abi/pre/gnu.ver (GLIBCXX_3.4.31): Export
>         _ZSt8to_charsPcS_DF128_, _ZSt8to_charsPcS_DF128_St12chars_format,
>         _ZSt8to_charsPcS_DF128_St12chars_formati and
>         _ZSt10from_charsPKcS0_RDF128_St12chars_format.
>         * src/c++17/floating_from_chars.cc (USE_STRTOF128_FOR_FROM_CHARS):
>         Define if needed.
>         (__strtof128): Declare.
>         (from_chars_impl): Handle _Float128.
>         (from_chars): New _Float128 overload if USE_STRTOF128_FOR_FROM_CHARS
>         is define.
>         * src/c++17/floating_to_chars.cc (__strfromf128): Declare.
>         (FLOAT128_TO_CHARS): Define even when _Float128 is supported and
>         wider than long double.
>         (F128_type): Use _Float128 for that case.
>         (floating_type_traits): Specialize for F128_type rather than
>         __float128.
>         (sprintf_ld): Add length argument.  Handle _Float128.
>         (__floating_to_chars_shortest, __floating_to_chars_precision):
>         Pass length to sprintf_ld.
>         (to_chars): Add _Float128 overloads for the F128_type being
>         _Float128 cases.
>         * testsuite/20_util/to_chars/float128_c++23.cc: New test.
>
> --- libstdc++-v3/include/std/charconv.jj        2022-10-31 22:20:39.475072806 +0100
> +++ libstdc++-v3/include/std/charconv   2022-11-01 16:48:50.693196228 +0100
> @@ -736,6 +736,27 @@ namespace __detail
>        __value = __val;
>      return __res;
>    }
> +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH)
> +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
> +  __extension__ from_chars_result
> +  from_chars(const char* __first, const char* __last, __ieee128& __value,
> +            chars_format __fmt = chars_format::general) noexcept;
> +
> +  inline from_chars_result
> +  from_chars(const char* __first, const char* __last, _Float128& __value,
> +            chars_format __fmt = chars_format::general) noexcept
> +  {
> +    __extension__ __ieee128 __val;
> +    from_chars_result __res = from_chars(__first, __last, __val, __fmt);
> +    if (__res.ec == errc{})
> +      __value = __val;
> +    return __res;
> +  }
> +#else
> +  from_chars_result
> +  from_chars(const char* __first, const char* __last, _Float128& __value,
> +            chars_format __fmt = chars_format::general) noexcept;
> +#endif
>  #endif
>
>  #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32) \
> @@ -851,6 +872,46 @@ namespace __detail
>      return to_chars(__first, __last, static_cast<long double>(__value), __fmt,
>                     __precision);
>    }
> +#elif defined(__STDCPP_FLOAT128_T__) && defined(_GLIBCXX_HAVE_FLOAT128_MATH)
> +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
> +  __extension__ to_chars_result
> +  to_chars(char* __first, char* __last, __float128 __value) noexcept;
> +  __extension__ to_chars_result
> +  to_chars(char* __first, char* __last, __float128 __value,
> +          chars_format __fmt) noexcept;
> +  __extension__ to_chars_result
> +  to_chars(char* __first, char* __last, __float128 __value,
> +          chars_format __fmt, int __precision) noexcept;
> +
> +  inline to_chars_result
> +  to_chars(char* __first, char* __last, _Float128 __value) noexcept
> +  {
> +    __extension__
> +    return to_chars(__first, __last, static_cast<__float128>(__value));
> +  }
> +  inline to_chars_result
> +  to_chars(char* __first, char* __last, _Float128 __value,
> +          chars_format __fmt) noexcept
> +  {
> +    __extension__
> +    return to_chars(__first, __last, static_cast<__float128>(__value), __fmt);
> +  }
> +  inline to_chars_result
> +  to_chars(char* __first, char* __last, _Float128 __value,
> +          chars_format __fmt, int __precision) noexcept
> +  {
> +    __extension__
> +    return to_chars(__first, __last, static_cast<__float128>(__value), __fmt,
> +                   __precision);
> +  }
> +#else
> +  to_chars_result to_chars(char* __first, char* __last, _Float128 __value)
> +    noexcept;
> +  to_chars_result to_chars(char* __first, char* __last, _Float128 __value,
> +                          chars_format __fmt) noexcept;
> +  to_chars_result to_chars(char* __first, char* __last, _Float128 __value,
> +                          chars_format __fmt, int __precision) noexcept;
> +#endif
>  #endif
>
>  #if defined(__STDCPP_BFLOAT16_T__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
> --- libstdc++-v3/config/abi/pre/gnu.ver.jj      2022-10-31 22:20:39.475072806 +0100
> +++ libstdc++-v3/config/abi/pre/gnu.ver 2022-11-01 17:00:57.682346445 +0100
> @@ -2450,6 +2450,10 @@ GLIBCXX_3.4.31 {
>      _ZSt21__to_chars_bfloat16_tPcS_fSt12chars_format;
>      _ZSt22__from_chars_float16_tPKcS0_RfSt12chars_format;
>      _ZSt23__from_chars_bfloat16_tPKcS0_RfSt12chars_format;
> +    _ZSt8to_charsPcS_DF128_;
> +    _ZSt8to_charsPcS_DF128_St12chars_format;
> +    _ZSt8to_charsPcS_DF128_St12chars_formati;
> +    _ZSt10from_charsPKcS0_RDF128_St12chars_format;
>  } GLIBCXX_3.4.30;
>
>  # Symbols in the support library (libsupc++) have their own tag.
> --- libstdc++-v3/src/c++17/floating_from_chars.cc.jj    2022-10-31 22:20:39.476072793 +0100
> +++ libstdc++-v3/src/c++17/floating_from_chars.cc       2022-11-01 16:59:23.999615740 +0100
> @@ -59,6 +59,14 @@
>  #endif
>  // strtold for __ieee128
>  extern "C" __ieee128 __strtoieee128(const char*, char**);
> +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \
> +      && defined(__GLIBC_PREREQ)
> +#define USE_STRTOF128_FOR_FROM_CHARS 1
> +extern "C" _Float128 __strtof128(const char*, char**)
> +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH
> +  __attribute__((__weak__))
> +#endif
> +  __asm ("strtof128");
>  #endif
>
>  #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
> @@ -618,6 +626,16 @@ namespace
>  # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
>         else if constexpr (is_same_v<T, __ieee128>)
>           tmpval = __strtoieee128(str, &endptr);
> +# elif defined(USE_STRTOF128_FOR_FROM_CHARS)
> +       else if constexpr (is_same_v<T, _Float128>)
> +         {
> +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH
> +           if (&__strtof128 == nullptr)
> +             tmpval = _Float128(std::strtold(str, &endptr);
> +           else
> +#endif
> +             tmpval = __strtof128(str, &endptr);
> +         }
>  # endif
>  #else
>         tmpval = std::strtod(str, &endptr);
> @@ -1232,6 +1250,14 @@ from_chars(const char* first, const char
>            chars_format fmt) noexcept
>  {
>    // fast_float doesn't support IEEE binary128 format, but we can use strtold.
> +  return from_chars_strtod(first, last, value, fmt);
> +}
> +#elif defined(USE_STRTOF128_FOR_FROM_CHARS)
> +from_chars_result
> +from_chars(const char* first, const char* last, _Float128& value,
> +          chars_format fmt) noexcept
> +{
> +  // fast_float doesn't support IEEE binary128 format, but we can use strtold.
>    return from_chars_strtod(first, last, value, fmt);
>  }
>  #endif
> --- libstdc++-v3/src/c++17/floating_to_chars.cc.jj      2022-11-01 12:16:14.352652455 +0100
> +++ libstdc++-v3/src/c++17/floating_to_chars.cc 2022-11-01 16:25:48.330968909 +0100
> @@ -43,6 +43,13 @@
>  #endif
>  // sprintf for __ieee128
>  extern "C" int __sprintfieee128(char*, const char*, ...);
> +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \
> +      && defined(__GLIBC_PREREQ)
> +extern "C" int __strfromf128(char*, size_t, const char*, _Float128)
> +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH
> +  __attribute__((__weak__))
> +#endif
> +  __asm ("strfromf128");
>  #endif
>
>  // This implementation crucially assumes float/double have the
> @@ -77,10 +84,11 @@ extern "C" int __sprintfieee128(char*, c
>  #if defined _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT && __FLT128_MANT_DIG__ == 113
>  // Define overloads of std::to_chars for __float128.
>  # define FLOAT128_TO_CHARS 1
> -#endif
> -
> -#ifdef FLOAT128_TO_CHARS
>  using F128_type = __float128;
> +#elif __FLT128_MANT_DIG__ == 113 && __LDBL_MANT_DIG__ != 113 \
> +      && defined(__GLIBC_PREREQ)
> +# define FLOAT128_TO_CHARS 1
> +using F128_type = _Float128;
>  #else
>  using F128_type = void;
>  #endif
> @@ -252,7 +260,7 @@ namespace
>
>  # ifdef FLOAT128_TO_CHARS
>    template<>
> -    struct floating_type_traits<__float128> : floating_type_traits_binary128
> +    struct floating_type_traits<F128_type> : floating_type_traits_binary128
>      { };
>  # endif
>  #endif
> @@ -1035,7 +1043,8 @@ namespace
>  #pragma GCC diagnostic ignored "-Wabi"
>    template<typename T, typename... Extra>
>    inline int
> -  sprintf_ld(char* buffer, const char* format_string, T value, Extra... args)
> +  sprintf_ld(char* buffer, size_t length __attribute__((unused)),
> +            const char* format_string, T value, Extra... args)
>    {
>      int len;
>
> @@ -1045,10 +1054,31 @@ namespace
>        fesetround(FE_TONEAREST); // We want round-to-nearest behavior.
>  #endif
>
> +#ifdef FLOAT128_TO_CHARS
>  #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
>      if constexpr (is_same_v<T, __ieee128>)
>        len = __sprintfieee128(buffer, format_string, args..., value);
>      else
> +#else
> +    if constexpr (is_same_v<T, _Float128>)
> +      {
> +#ifndef _GLIBCXX_HAVE_FLOAT128_MATH
> +       if (&__strfromf128 == nullptr)
> +         len = sprintf(buffer, format_string, args..., (long double)value);
> +       else
> +#endif
> +       if constexpr (sizeof...(args) == 0)
> +         len = __strfromf128(buffer, length, "%.0f", value);
> +       else
> +         {
> +           // strfromf128 unfortunately doesn't allow .*
> +           char fmt[3 * sizeof(int) + 6];
> +           sprintf(fmt, "%%.%d%c", args..., int(format_string[4]));
> +           len = __strfromf128(buffer, length, fmt, value);
> +         }
> +      }
> +    else
> +#endif
>  #endif
>      len = sprintf(buffer, format_string, args..., value);
>
> @@ -1206,8 +1236,10 @@ template<typename T>
>             // can avoid this if we use sprintf to write all but the last
>             // digit, and carefully compute and write the last digit
>             // ourselves.
> -           char buffer[expected_output_length+1];
> -           const int output_length = sprintf_ld(buffer, "%.0Lf", value);
> +           char buffer[expected_output_length + 1];
> +           const int output_length = sprintf_ld(buffer,
> +                                                expected_output_length + 1,
> +                                                "%.0Lf", value);
>             __glibcxx_assert(output_length == expected_output_length);
>             memcpy(first, buffer, output_length);
>             return {first + output_length, errc{}};
> @@ -1397,9 +1429,10 @@ template<typename T>
>           __builtin_unreachable();
>
>         // Do the sprintf into the local buffer.
> -       char buffer[output_length_upper_bound+1];
> +       char buffer[output_length_upper_bound + 1];
>         int output_length
> -         = sprintf_ld(buffer, output_specifier, value, effective_precision);
> +         = sprintf_ld(buffer, output_length_upper_bound + 1, output_specifier,
> +                      value, effective_precision);
>         __glibcxx_assert(output_length <= output_length_upper_bound);
>
>         if (effective_precision > 0)
> @@ -1799,6 +1832,7 @@ to_chars(char* first, char* last, long d
>  }
>
>  #ifdef FLOAT128_TO_CHARS
> +#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
>  to_chars_result
>  to_chars(char* first, char* last, __float128 value) noexcept
>  {
> @@ -1817,6 +1851,26 @@ to_chars(char* first, char* last, __floa
>  {
>    return __floating_to_chars_precision(first, last, value, fmt, precision);
>  }
> +#else
> +to_chars_result
> +to_chars(char* first, char* last, _Float128 value) noexcept
> +{
> +  return __floating_to_chars_shortest(first, last, value, chars_format{});
> +}
> +
> +to_chars_result
> +to_chars(char* first, char* last, _Float128 value, chars_format fmt) noexcept
> +{
> +  return __floating_to_chars_shortest(first, last, value, fmt);
> +}
> +
> +to_chars_result
> +to_chars(char* first, char* last, _Float128 value, chars_format fmt,
> +        int precision) noexcept
> +{
> +  return __floating_to_chars_precision(first, last, value, fmt, precision);
> +}
> +#endif
>  #endif
>
>  // Entrypoints for 16-bit floats.
> --- libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc.jj        2022-11-01 17:04:19.895606130 +0100
> +++ libstdc++-v3/testsuite/20_util/to_chars/float128_c++23.cc   2022-11-01 20:47:15.254989646 +0100
> @@ -0,0 +1,105 @@
> +// Copyright (C) 2022 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// You should have received a copy of the GNU General Public License along
> +// with this library; see the file COPYING3.  If not see
> +// <http://www.gnu.org/licenses/>.
> +
> +// { 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 <charconv>
> +#include <stdfloat>
> +#include <limits>
> +#include <numbers>
> +#include <testsuite_hooks.h>
> +
> +#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<std::float128_t>::denorm_min(),
> +    std::numeric_limits<std::float128_t>::min(),
> +    0.0f128,
> +    -42.0f128,
> +    1234.5678912345f128,
> +    std::numbers::e_v<std::float128_t>,
> +    std::numbers::log2e_v<std::float128_t>,
> +    std::numbers::log10e_v<std::float128_t>,
> +    std::numbers::pi_v<std::float128_t>,
> +    std::numbers::inv_pi_v<std::float128_t>,
> +    std::numbers::inv_sqrtpi_v<std::float128_t>,
> +    std::numbers::ln2_v<std::float128_t>,
> +    std::numbers::ln10_v<std::float128_t>,
> +    std::numbers::sqrt2_v<std::float128_t>,
> +    std::numbers::sqrt3_v<std::float128_t>,
> +    std::numbers::inv_sqrt3_v<std::float128_t>,
> +    std::numbers::egamma_v<std::float128_t>,
> +    std::numbers::phi_v<std::float128_t>,
> +    std::numeric_limits<std::float128_t>::max()
> +  };
> +  char str1[10000], str2[10000];
> +  for (auto u : tests)
> +    {
> +      auto [ptr1, ec1] = std::to_chars(str1, str1 + sizeof(str1), u, fmt);
> +      VERIFY( ec1 == std::errc() );
> +//    std::cout << i << ' ' << std::string_view (str1, ptr1) << '\n';
> +      if (fmt == std::chars_format::fixed)
> +       {
> +         auto [ptr2, ec2] = std::to_chars(str2, str2 + (ptr1 - str1), u, fmt);
> +         VERIFY( ec2 == std::errc() && ptr2 - str2 == ptr1 - str1 );
> +         auto [ptr3, ec3] = std::to_chars(str2, str2 + (ptr1 - str1 - 1), u, fmt);
> +         VERIFY( ec3 != std::errc() );
> +       }
> +      std::float128_t v;
> +      auto [ptr4, ec4] = std::from_chars(str1, ptr1, v,
> +                                        fmt == std::chars_format{}
> +                                        ? std::chars_format::general : fmt);
> +      VERIFY( ec4 == std::errc() && ptr4 == ptr1 );
> +      VERIFY( u == v );
> +
> +      auto [ptr5, ec5] = std::to_chars(str1, str1 + sizeof(str1), u, fmt, 90);
> +      VERIFY( ec5 == std::errc() );
> +//    std::cout << i << ' ' << std::string_view (str1, ptr5) << '\n';
> +      v = 4.0f128;
> +      auto [ptr6, ec6] = std::from_chars(str1, ptr5, v,
> +                                        fmt == std::chars_format{}
> +                                        ? std::chars_format::general : fmt);
> +      VERIFY( ec6 == std::errc() && ptr6 == ptr5 );
> +      if (fmt == std::chars_format::fixed && u > 0.0f128 && u < 0.000001f128)
> +       VERIFY( v == 0.0 );
> +      else
> +       VERIFY( u == v );
> +    }
> +}
> +#endif
> +
> +int
> +main()
> +{
> +#if defined(__STDCPP_FLOAT128_T__) \
> +    && (defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) \
> +       || defined(_GLIBCXX_HAVE_FLOAT128_MATH))
> +  test();
> +  test(std::chars_format::fixed);
> +  test(std::chars_format::scientific);
> +  test(std::chars_format::general);
> +  test(std::chars_format::hex);
> +#endif
> +}
>
>         Jakub
>


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

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-02  9:25 Jakub Jelinek
2022-11-07 13:45 ` Jonathan Wakely [this message]
2022-11-07 16:11 ` Joseph Myers
2022-11-07 17:48   ` Jonathan Wakely
2022-11-07 17:52     ` Joseph Myers
2022-11-07 19:13     ` Jakub Jelinek
2022-11-07 22:43       ` Jonathan Wakely
2022-11-08  1:41         ` Joseph Myers
2022-11-08  8:43           ` Jakub Jelinek

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=CACb0b4nNR5vqBe_H5nWv+JFhzveD8Jjypyrqruse-ko5yUn3Yw@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).