]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libstdc++-v3/src/c++17/floating_from_chars.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / floating_from_chars.cc
index 26b69a3852150f6f2b07116e48a392fe1c6d5120..5d18ca32dbb3b23d71b0f194e6af935ed6d01766 100644 (file)
@@ -1,6 +1,6 @@
 // std::from_chars implementation for floating-point types -*- C++ -*-
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2023 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
 // 23.2.9  Primitive numeric input conversion [utility.from.chars]
 //
 
+// Prefer to use std::pmr::string if possible, which requires the cxx11 ABI.
+#define _GLIBCXX_USE_CXX11_ABI 1
+
+#include <array>
 #include <charconv>
+#include <bit>
 #include <string>
 #include <memory_resource>
 #include <cfenv>
+#include <cfloat>
 #include <cmath>
 #include <cstdlib>
 #include <cstring>
-#include <cctype>
 #include <locale.h>
 #include <bits/functexcept.h>
 #if _GLIBCXX_HAVE_XLOCALE_H
 #endif
 
 #if _GLIBCXX_HAVE_USELOCALE
+// FIXME: This should be reimplemented so it doesn't use strtod and newlocale.
+// That will avoid the need for any memory allocation, meaning that the
+// non-conforming errc::not_enough_memory result cannot happen.
+# define USE_STRTOD_FOR_FROM_CHARS 1
+#endif
+
+#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
+#ifndef __LONG_DOUBLE_IBM128__
+#error "floating_from_chars.cc must be compiled with -mabi=ibmlongdouble"
+#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**)
+  __asm ("strtof128")
+#ifndef _GLIBCXX_HAVE_FLOAT128_MATH
+  __attribute__((__weak__))
+#endif
+  ;
+#endif
+
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
+    && __SIZE_WIDTH__ >= 32
+# define USE_LIB_FAST_FLOAT 1
+# if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+// No need to use strtold.
+#  undef USE_STRTOD_FOR_FROM_CHARS
+# endif
+#endif
+
+#if USE_LIB_FAST_FLOAT
+# define FASTFLOAT_DEBUG_ASSERT __glibcxx_assert
+namespace
+{
+# include "fast_float/fast_float.h"
+
+namespace fast_float
+{
+
+  // Wrappers around float for std::{,b}float16_t promoted to float.
+  struct floating_type_float16_t
+  {
+    float* x;
+    uint16_t bits;
+  };
+  struct floating_type_bfloat16_t
+  {
+    float* x;
+    uint16_t bits;
+  };
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::mantissa_explicit_bits()
+  { return 10; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits()
+  { return 7; }
+
+  // 10 bits of stored mantissa, pow(5,q) <= 0x4p+10 implies q <= 5
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::max_exponent_round_to_even()
+  { return 5; }
+
+  // 7 bits of stored mantissa, pow(5,q) <= 0x4p+7 implies q <= 3
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::max_exponent_round_to_even()
+  { return 3; }
+
+  // 10 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+11 implies q >= -22
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::min_exponent_round_to_even()
+  { return -22; }
+
+  // 7 bits of stored mantissa, pow(5,-q) < 0x1p+64 / 0x1p+8 implies q >= -24
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::min_exponent_round_to_even()
+  { return -24; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::minimum_exponent()
+  { return -15; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::minimum_exponent()
+  { return -127; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::infinite_power()
+  { return 0x1F; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::infinite_power()
+  { return 0xFF; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::sign_index()
+  { return 15; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::sign_index()
+  { return 15; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::largest_power_of_ten()
+  { return 4; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::largest_power_of_ten()
+  { return 38; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_float16_t>::smallest_power_of_ten()
+  { return -27; }
+
+  template<>
+  constexpr int
+  binary_format<floating_type_bfloat16_t>::smallest_power_of_ten()
+  { return -60; }
+
+  template<>
+  constexpr size_t
+  binary_format<floating_type_float16_t>::max_digits()
+  { return 22; }
+
+  template<>
+  constexpr size_t
+  binary_format<floating_type_bfloat16_t>::max_digits()
+  { return 98; }
+
+  // negative_digit_comp converts adjusted_mantissa to the (originally only)
+  // floating type and immediately back with slight tweaks (e.g. explicit
+  // leading bit instead of implicit for normals).
+  // Avoid going through the floating point type.
+  template<>
+  fastfloat_really_inline void
+  to_float<floating_type_float16_t>(bool negative, adjusted_mantissa am,
+                                   floating_type_float16_t &value)
+  {
+    constexpr int mantissa_bits
+      = binary_format<floating_type_float16_t>::mantissa_explicit_bits();
+    value.bits = (am.mantissa
+                 | (uint16_t(am.power2) << mantissa_bits)
+                 | (negative ? 0x8000 : 0));
+  }
+
+  template<>
+  fastfloat_really_inline void
+  to_float<floating_type_bfloat16_t>(bool negative, adjusted_mantissa am,
+                                    floating_type_bfloat16_t &value)
+  {
+    constexpr int mantissa_bits
+      = binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits();
+    value.bits = (am.mantissa
+                 | (uint16_t(am.power2) << mantissa_bits)
+                 | (negative ? 0x8000 : 0));
+  }
+
+  template <>
+  fastfloat_really_inline adjusted_mantissa
+  to_extended<floating_type_float16_t>(floating_type_float16_t value) noexcept
+  {
+    adjusted_mantissa am;
+    constexpr int mantissa_bits
+      = binary_format<floating_type_float16_t>::mantissa_explicit_bits();
+    int32_t bias
+      = (mantissa_bits
+        - binary_format<floating_type_float16_t>::minimum_exponent());
+    constexpr uint16_t exponent_mask = 0x7C00;
+    constexpr uint16_t mantissa_mask = 0x03FF;
+    constexpr uint16_t hidden_bit_mask = 0x0400;
+    if ((value.bits & exponent_mask) == 0) {
+      // denormal
+      am.power2 = 1 - bias;
+      am.mantissa = value.bits & mantissa_mask;
+    } else {
+      // normal
+      am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits);
+      am.power2 -= bias;
+      am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask;
+    }
+    return am;
+  }
+
+  template <>
+  fastfloat_really_inline adjusted_mantissa
+  to_extended<floating_type_bfloat16_t>(floating_type_bfloat16_t value) noexcept
+  {
+    adjusted_mantissa am;
+    constexpr int mantissa_bits
+      = binary_format<floating_type_bfloat16_t>::mantissa_explicit_bits();
+    int32_t bias
+      = (mantissa_bits
+        - binary_format<floating_type_bfloat16_t>::minimum_exponent());
+    constexpr uint16_t exponent_mask = 0x7F80;
+    constexpr uint16_t mantissa_mask = 0x007F;
+    constexpr uint16_t hidden_bit_mask = 0x0080;
+    if ((value.bits & exponent_mask) == 0) {
+      // denormal
+      am.power2 = 1 - bias;
+      am.mantissa = value.bits & mantissa_mask;
+    } else {
+      // normal
+      am.power2 = int32_t((value.bits & exponent_mask) >> mantissa_bits);
+      am.power2 -= bias;
+      am.mantissa = (value.bits & mantissa_mask) | hidden_bit_mask;
+    }
+    return am;
+  }
+
+  // Like fast_float.h from_chars_advanced, but for 16-bit float.
+  template<typename T>
+  from_chars_result
+  from_chars_16(const char* first, const char* last, T &value,
+               chars_format fmt) noexcept
+  {
+    parse_options options{fmt};
+
+    from_chars_result answer;
+    if (first == last)
+      {
+       answer.ec = std::errc::invalid_argument;
+       answer.ptr = first;
+       return answer;
+      }
+
+    parsed_number_string pns = parse_number_string(first, last, options);
+    if (!pns.valid)
+      return detail::parse_infnan(first, last, *value.x);
+
+    answer.ec = std::errc();
+    answer.ptr = pns.lastmatch;
+
+    adjusted_mantissa am
+      = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
+    if (pns.too_many_digits && am.power2 >= 0)
+      {
+       if (am != compute_float<binary_format<T>>(pns.exponent,
+                                                 pns.mantissa + 1))
+         am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
+      }
+
+    // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
+    // and we have an invalid power (am.power2 < 0),
+    // then we need to go the long way around again.  This is very uncommon.
+    if (am.power2 < 0)
+      am = digit_comp<T>(pns, am);
+
+    if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0)
+       || am.power2 == binary_format<T>::infinite_power())
+      {
+       // In case of over/underflow, return result_out_of_range and don't
+       // modify value, as per [charconv.from.chars]/1.  Note that LWG 3081 wants
+       // to modify value in this case too.
+       answer.ec = std::errc::result_out_of_range;
+       return answer;
+      }
+
+    // Transform the {,b}float16_t to float32_t before to_float.
+    if constexpr (std::is_same_v<T, floating_type_float16_t>)
+      {
+       if (am.power2 == 0)
+         {
+           if (am.mantissa)
+             {
+               int n = (std::numeric_limits<unsigned int>::digits
+                        - __builtin_clz (am.mantissa)) - 1;
+               am.mantissa &= ~(static_cast<decltype(am.mantissa)>(1) << n);
+               am.mantissa <<= (binary_format<float>::mantissa_explicit_bits()
+                                - n);
+               am.power2 = n + 0x67;
+             }
+         }
+       else
+         {
+           am.mantissa <<= 13;
+           am.power2 += 0x70;
+         }
+      }
+    else
+      am.mantissa <<= 16;
+    to_float(pns.negative, am, *value.x);
+    return answer;
+  }
+} // fast_float
+
+} // anon namespace
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 namespace
 {
+#if USE_STRTOD_FOR_FROM_CHARS
   // A memory resource with a static buffer that can be used for small
   // allocations. At most one allocation using the freestore can be done
   // if the static buffer is insufficient. The callers below only require
@@ -64,7 +376,6 @@ namespace
        return m_buf + std::__exchange(m_bytes, m_bytes + bytes);
 
       __glibcxx_assert(m_ptr == nullptr);
-      __glibcxx_assert(alignment != 1);
 
       m_ptr = operator new(bytes);
       m_bytes = bytes;
@@ -87,6 +398,12 @@ namespace
     void* m_ptr = nullptr;
   };
 
+#if _GLIBCXX_USE_CXX11_ABI
+  using buffered_string = std::pmr::string;
+#else
+  using buffered_string = std::string;
+#endif
+
   inline bool valid_fmt(chars_format fmt)
   {
     return fmt != chars_format{}
@@ -99,10 +416,10 @@ namespace
 
   // Find initial portion of [first, last) containing a floating-point number.
   // The string `digits` is either `dec_digits` or `hex_digits`
-  // and `exp` is 'e' or 'p' or '\0'.
+  // and `exp` is "eE", "pP" or NULL.
   const char*
   find_end_of_float(const char* first, const char* last, const char* digits,
-                   char exp)
+                   const char *exp)
   {
     while (first < last && strchr(digits, *first) != nullptr)
       ++first;
@@ -112,7 +429,7 @@ namespace
        while (first < last && strchr(digits, *first))
          ++first;
       }
-    if (first < last && exp != 0 && std::tolower((unsigned char)*first) == exp)
+    if (first < last && exp != nullptr && (*first == exp[0] || *first == exp[1]))
       {
        ++first;
        if (first < last && (*first == '-' || *first == '+'))
@@ -130,7 +447,7 @@ namespace
   // Returns a nullptr if a valid pattern is not present.
   const char*
   pattern(const char* const first, const char* last,
-         chars_format& fmt, pmr::string& buf)
+         chars_format& fmt, buffered_string& buf)
   {
     // fmt has the value of one of the enumerators of chars_format.
     __glibcxx_assert(valid_fmt(fmt));
@@ -194,7 +511,7 @@ namespace
 
        if ((last - first + 2) > buffer_resource::guaranteed_capacity())
          {
-           last = find_end_of_float(first + neg, last, digits, 'p');
+           last = find_end_of_float(first + neg, last, digits, "pP");
 #ifndef __cpp_exceptions
            if ((last - first + 2) > buffer_resource::guaranteed_capacity())
              {
@@ -218,7 +535,7 @@ namespace
        if ((last - first) > buffer_resource::guaranteed_capacity())
          {
            last = find_end_of_float(first + neg, last, digits,
-                                    "e"[fmt == chars_format::fixed]);
+                                    fmt == chars_format::fixed ? nullptr : "eE");
 #ifndef __cpp_exceptions
            if ((last - first) > buffer_resource::guaranteed_capacity())
              {
@@ -286,11 +603,11 @@ namespace
   ptrdiff_t
   from_chars_impl(const char* str, T& value, errc& ec) noexcept
   {
-    if (locale_t loc = ::newlocale(LC_ALL, "C", (locale_t)0)) [[likely]]
+    if (locale_t loc = ::newlocale(LC_ALL_MASK, "C", (locale_t)0)) [[likely]]
       {
        locale_t orig = ::uselocale(loc);
 
-#if _GLIBCXX_USE_C99_FENV_TR1
+#if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
        const int rounding = std::fegetround();
        if (rounding != FE_TONEAREST)
          std::fesetround(FE_TONEAREST);
@@ -307,12 +624,26 @@ namespace
          tmpval = std::strtod(str, &endptr);
        else if constexpr (is_same_v<T, long double>)
          tmpval = std::strtold(str, &endptr);
+# 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);
 #endif
        const int conv_errno = std::__exchange(errno, save_errno);
 
-#if _GLIBCXX_USE_C99_FENV_TR1
+#if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
        if (rounding != FE_TONEAREST)
          std::fesetround(rounding);
 #endif
@@ -323,10 +654,15 @@ namespace
        const ptrdiff_t n = endptr - str;
        if (conv_errno == ERANGE) [[unlikely]]
          {
-           if (isinf(tmpval)) // overflow
+           if (__builtin_isinf(tmpval)) // overflow
              ec = errc::result_out_of_range;
-           else // underflow (LWG 3081 wants to set value = tmpval here)
+           else if (tmpval == 0) // underflow (LWG 3081 wants to set value = tmpval here)
              ec = errc::result_out_of_range;
+           else // denormal value
+             {
+               value = tmpval;
+               ec = errc();
+             }
          }
        else if (n)
          {
@@ -359,73 +695,564 @@ namespace
     return result;
   }
 
+#if ! _GLIBCXX_USE_CXX11_ABI
+  inline bool
+  reserve_string(std::string& s) noexcept
+  {
+    __try
+      {
+       s.reserve(buffer_resource::guaranteed_capacity());
+      }
+    __catch (const std::bad_alloc&)
+      {
+       return false;
+      }
+    return true;
+  }
+#endif
+
+  template<typename T>
+  from_chars_result
+  from_chars_strtod(const char* first, const char* last, T& value,
+                   chars_format fmt) noexcept
+  {
+    errc ec = errc::invalid_argument;
+#if _GLIBCXX_USE_CXX11_ABI
+    buffer_resource mr;
+    pmr::string buf(&mr);
+#else
+    string buf;
+    if (!reserve_string(buf))
+      return make_result(first, 0, {}, ec);
+#endif
+    size_t len = 0;
+    __try
+      {
+       if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
+         len = from_chars_impl(pat, value, ec);
+      }
+    __catch (const std::bad_alloc&)
+      {
+       fmt = chars_format{};
+      }
+    return make_result(first, len, fmt, ec);
+  }
+#endif // USE_STRTOD_FOR_FROM_CHARS
+
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
+  // Return true iff [FIRST,LAST) begins with PREFIX, ignoring case.
+  // PREFIX is assumed to not contain any uppercase letters.
+  bool
+  starts_with_ci(const char* first, const char* last, string_view prefix)
+  {
+    __glibcxx_requires_valid_range(first, last);
+
+    // A lookup table that maps uppercase letters to lowercase and
+    // is otherwise the identity mapping.
+    static constexpr auto upper_to_lower_table = [] {
+      constexpr unsigned char lower_letters[27] = "abcdefghijklmnopqrstuvwxyz";
+      constexpr unsigned char upper_letters[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+      std::array<unsigned char, (1u << __CHAR_BIT__)> table = {};
+      for (unsigned i = 0; i < table.size(); ++i)
+       table[i] = i;
+      for (unsigned i = 0; i < 26; ++i)
+       table[upper_letters[i]] = lower_letters[i];
+      return table;
+    }();
+
+    if (last - first < static_cast<ptrdiff_t>(prefix.length()))
+      return false;
+
+    for (const unsigned char pch : prefix)
+      {
+       // __glibcxx_assert(pch == upper_to_lower_table[pch]);
+       const unsigned char ch = *first;
+       if (ch != pch && upper_to_lower_table[ch] != pch)
+         return false;
+       ++first;
+      }
+
+    return true;
+  }
+
+  // An implementation of hexadecimal float parsing for binary32/64.
+  template<typename T>
+  from_chars_result
+  __floating_from_chars_hex(const char* first, const char* last, T& value)
+  {
+    using uint_t = conditional_t<is_same_v<T, float>, uint32_t,
+                                conditional_t<is_same_v<T, double>, uint64_t,
+                                              uint16_t>>;
+#if USE_LIB_FAST_FLOAT
+    constexpr int mantissa_bits
+      = fast_float::binary_format<T>::mantissa_explicit_bits();
+    constexpr int exponent_bits
+      = is_same_v<T, double> ? 11
+       : is_same_v<T, fast_float::floating_type_float16_t> ? 5 : 8;
+#else
+    constexpr int mantissa_bits = is_same_v<T, float> ? 23 : 52;
+    constexpr int exponent_bits = is_same_v<T, float> ? 8 : 11;
+#endif
+    constexpr int exponent_bias = (1 << (exponent_bits - 1)) - 1;
+
+    __glibcxx_requires_valid_range(first, last);
+    if (first == last)
+      return {first, errc::invalid_argument};
+
+    // Consume the sign bit.
+    const char* const orig_first = first;
+    bool sign_bit = false;
+    if (*first == '-')
+      {
+       sign_bit = true;
+       ++first;
+      }
+
+    // Handle "inf", "infinity", "NaN" and variants thereof.
+    if (first != last)
+      if (*first == 'i' || *first == 'I' || *first == 'n' || *first == 'N') [[unlikely]]
+       {
+         if (starts_with_ci(first, last, "inf"sv))
+           {
+             first += strlen("inf");
+             if (starts_with_ci(first, last, "inity"sv))
+               first += strlen("inity");
+
+             if constexpr (is_same_v<T, float> || is_same_v<T, double>)
+               {
+                 uint_t result = 0;
+                 result |= sign_bit;
+                 result <<= exponent_bits;
+                 result |= (1ull << exponent_bits) - 1;
+                 result <<= mantissa_bits;
+                 memcpy(&value, &result, sizeof(result));
+               }
+             else
+               {
+                 // float +/-Inf.
+                 uint32_t result = 0x7F800000 | (sign_bit ? 0x80000000U : 0);
+                 memcpy(value.x, &result, sizeof(result));
+               }
+
+             return {first, errc{}};
+           }
+         else if (starts_with_ci(first, last, "nan"))
+           {
+             first += strlen("nan");
+
+             if (first != last && *first == '(')
+               {
+                 // Tentatively consume the '(' as we look for an optional
+                 // n-char-sequence followed by a ')'.
+                 const char* const fallback_first = first;
+                 for (;;)
+                   {
+                     ++first;
+                     if (first == last)
+                       {
+                         first = fallback_first;
+                         break;
+                       }
+
+                     char ch = *first;
+                     if (ch == ')')
+                       {
+                         ++first;
+                         break;
+                       }
+                     else if (ch == '_'
+                              || __detail::__from_chars_alnum_to_val(ch) < 127)
+                       continue;
+                     else
+                       {
+                         first = fallback_first;
+                         break;
+                       }
+                   }
+               }
+
+             // We make the implementation-defined decision of ignoring the
+             // sign bit and the n-char-sequence when assembling the NaN.
+             if constexpr (is_same_v<T, float> || is_same_v<T, double>)
+               {
+                 uint_t result = 0;
+                 result <<= exponent_bits;
+                 result |= (1ull << exponent_bits) - 1;
+                 result <<= mantissa_bits;
+                 result |= (1ull << (mantissa_bits - 1)) | 1;
+                 memcpy(&value, &result, sizeof(result));
+               }
+             else
+               {
+                 // float qNaN.
+                 uint32_t result = 0x7FC00001;
+                 memcpy(value.x, &result, sizeof(result));
+               }
+
+             return {first, errc{}};
+           }
+       }
+
+    // Consume all insignificant leading zeros in the whole part of the
+    // mantissa.
+    bool seen_hexit = false;
+    while (first != last && *first == '0')
+      {
+       seen_hexit = true;
+       ++first;
+      }
+
+    // Now consume the rest of the written mantissa, populating MANTISSA with
+    // the first MANTISSA_BITS+k significant bits of the written mantissa, where
+    // 1 <= k <= 4 is the bit width of the leading significant written hexit.
+    //
+    // Examples:
+    //  After parsing "1.2f3", MANTISSA is 0x12f30000000000 (bit_width=52+1).
+    //  After parsing ".0000f0e", MANTISSA is 0xf0e00000000000 (bit_width=52+4).
+    //  After parsing ".1234567890abcd8", MANTISSA is 0x1234567890abcd (bit_width=52+1)
+    //   and MIDPOINT_BIT is true (and NONZERO_TAIL is false).
+    uint_t mantissa = 0;
+    int mantissa_idx = mantissa_bits; // The current bit index into MANTISSA
+                                      // into which we'll write the next hexit.
+    int exponent_adjustment = 0; // How much we'd have to adjust the written
+                                // exponent in order to represent the mantissa
+                                // in scientific form h.hhhhhhhhhhhhh.
+    bool midpoint_bit = false; // Whether the MANTISSA_BITS+k+1 significant
+                              // bit is set in the written mantissa.
+    bool nonzero_tail = false; // Whether some bit thereafter is set in the
+                              // written mantissa.
+    bool seen_decimal_point = false;
+    for (; first != last; ++first)
+      {
+       char ch = *first;
+       if (ch == '.' && !seen_decimal_point)
+         {
+           seen_decimal_point = true;
+           continue;
+         }
+
+       int hexit = __detail::__from_chars_alnum_to_val(ch);
+       if (hexit >= 16)
+         break;
+       seen_hexit = true;
+
+       if (!seen_decimal_point && mantissa != 0)
+         exponent_adjustment += 4;
+       else if (seen_decimal_point && mantissa == 0)
+         {
+           exponent_adjustment -= 4;
+           if (hexit == 0x0)
+             continue;
+         }
+
+       if (mantissa_idx >= 0)
+         mantissa |= uint_t(hexit) << mantissa_idx;
+       else if (mantissa_idx >= -4)
+         {
+           if constexpr (is_same_v<T, float>
+#if USE_LIB_FAST_FLOAT
+                         || is_same_v<T,
+                                      fast_float::floating_type_bfloat16_t>
+#endif
+                        )
+             {
+               __glibcxx_assert(mantissa_idx == -1);
+               mantissa |= hexit >> 1;
+               midpoint_bit = (hexit & 0b0001) != 0;
+             }
+           else if constexpr (is_same_v<T, double>)
+             {
+               __glibcxx_assert(mantissa_idx == -4);
+               midpoint_bit = (hexit & 0b1000) != 0;
+               nonzero_tail = (hexit & 0b0111) != 0;
+             }
+           else
+             {
+               __glibcxx_assert(mantissa_idx == -2);
+               mantissa |= hexit >> 2;
+               midpoint_bit = (hexit & 0b0010) != 0;
+               nonzero_tail = (hexit & 0b0001) != 0;
+             }
+         }
+       else
+         nonzero_tail |= (hexit != 0x0);
+
+       mantissa_idx -= 4;
+      }
+    if (mantissa != 0)
+      __glibcxx_assert(__bit_width(mantissa) >= mantissa_bits + 1
+                      && __bit_width(mantissa) <= mantissa_bits + 4);
+    else
+      __glibcxx_assert(!midpoint_bit && !nonzero_tail);
+
+    if (!seen_hexit)
+      // If we haven't seen any hexit at this point, the parse failed.
+      return {orig_first, errc::invalid_argument};
+
+    // Parse the written exponent.
+    int written_exponent = 0;
+    if (first != last && (*first == 'p' || *first == 'P'))
+      {
+       // Tentatively consume the 'p' and try to parse a decimal number.
+       const char* const fallback_first = first;
+       ++first;
+       if (first != last && *first == '+')
+         ++first;
+       from_chars_result fcr = from_chars(first, last, written_exponent, 10);
+       if (fcr.ptr == first)
+         // The parse failed, so undo consuming the 'p' and carry on as if the
+         // exponent was omitted (i.e. is 0).
+         first = fallback_first;
+       else
+         {
+           first = fcr.ptr;
+           if (mantissa != 0 && fcr.ec == errc::result_out_of_range)
+             // Punt on very large exponents for now. FIXME
+             return {first, errc::result_out_of_range};
+         }
+      }
+    int biased_exponent = written_exponent + exponent_bias;
+    if (exponent_adjustment != 0)
+      // The mantissa wasn't written in scientific form.  Adjust the exponent
+      // so that we may assume scientific form.
+      //
+      // Examples;
+      //  For input "a.bcp5", EXPONENT_ADJUSTMENT would be 0 since this
+      //   written mantissa is already in scientific form.
+      //  For input "ab.cp5", EXPONENT_ADJUSTMENT would be 4 since the
+      //   scientific form is "a.bcp9".
+      //  For input 0.0abcp5", EXPONENT_ADJUSTMENT would be -8 since the
+      //   scientific form is "a.bcp-3".
+      biased_exponent += exponent_adjustment;
+
+    // Shifts the mantissa to the right by AMOUNT while updating
+    // BIASED_EXPONENT, MIDPOINT_BIT and NONZERO_TAIL accordingly.
+    auto shift_mantissa = [&] (int amount) {
+      __glibcxx_assert(amount >= 0);
+      if (amount > mantissa_bits + 1)
+       {
+         // Shifting the mantissa by an amount greater than its precision.
+         nonzero_tail |= midpoint_bit;
+         nonzero_tail |= mantissa != 0;
+         midpoint_bit = false;
+         mantissa = 0;
+         biased_exponent += amount;
+       }
+      else if (amount != 0)
+       {
+         nonzero_tail |= midpoint_bit;
+         nonzero_tail |= (mantissa & ((1ull << (amount - 1)) - 1)) != 0;
+         midpoint_bit = (mantissa & (1ull << (amount - 1))) != 0;
+         mantissa >>= amount;
+         biased_exponent += amount;
+       }
+    };
+
+    if (mantissa != 0)
+      {
+       // If the leading hexit is not '1', shift MANTISSA to make it so.
+       // This normalizes input like "4.08p0" into "1.02p2".
+       const int leading_hexit = mantissa >> mantissa_bits;
+       const int leading_hexit_width = __bit_width(leading_hexit); // FIXME: optimize?
+       __glibcxx_assert(leading_hexit_width >= 1 && leading_hexit_width <= 4);
+       shift_mantissa(leading_hexit_width - 1);
+       // After this adjustment, we can assume the leading hexit is '1'.
+       __glibcxx_assert((mantissa >> mantissa_bits) == 0x1);
+      }
+
+    if (biased_exponent <= 0)
+      {
+       // This number is too small to be represented as a normal number, so
+       // try for a subnormal number by shifting the mantissa sufficiently.
+       // We need to shift by 1 more than -BIASED_EXPONENT because the leading
+       // mantissa bit is omitted in the representation of a normal number but
+       // not in a subnormal number.
+       shift_mantissa(-biased_exponent + 1);
+       __glibcxx_assert(!(mantissa & (1ull << mantissa_bits)));
+       __glibcxx_assert(biased_exponent == 1);
+       biased_exponent = 0;
+      }
+
+    // Perform round-to-nearest, tie-to-even rounding according to
+    // MIDPOINT_BIT and NONZERO_TAIL.
+    if (midpoint_bit && (nonzero_tail || (mantissa % 2) != 0))
+      {
+       // Rounding away from zero.
+       ++mantissa;
+       midpoint_bit = false;
+       nonzero_tail = false;
+
+       // Deal with a couple of corner cases after rounding.
+       if (mantissa == (1ull << mantissa_bits))
+         {
+           // We rounded the subnormal number 1.fffffffffffff...p-1023
+           // up to the normal number 1p-1022.
+           __glibcxx_assert(biased_exponent == 0);
+           ++biased_exponent;
+         }
+       else if (mantissa & (1ull << (mantissa_bits + 1)))
+         {
+           // We rounded the normal number 1.fffffffffffff8pN (with maximal
+           // mantissa) up to to 1p(N+1).
+           mantissa >>= 1;
+           ++biased_exponent;
+         }
+      }
+    else
+      {
+       // Rounding toward zero.
+
+       if (mantissa == 0 && (midpoint_bit || nonzero_tail))
+         {
+           // A nonzero number that rounds to zero is unrepresentable.
+           __glibcxx_assert(biased_exponent == 0);
+           return {first, errc::result_out_of_range};
+         }
+
+       midpoint_bit = false;
+       nonzero_tail = false;
+      }
+
+    if (mantissa != 0 && biased_exponent >= (1 << exponent_bits) - 1)
+      // The exponent of this number is too large to be representable.
+      return {first, errc::result_out_of_range};
+
+    uint_t result = 0;
+    if (mantissa == 0)
+      {
+       // Assemble a (possibly signed) zero.
+       if (sign_bit)
+         result |= 1ull << (exponent_bits + mantissa_bits);
+      }
+    else
+      {
+       // Assemble a nonzero normal or subnormal value.
+       result |= sign_bit;
+       result <<= exponent_bits;
+       result |= biased_exponent;
+       result <<= mantissa_bits;
+       result |= mantissa & ((1ull << mantissa_bits) - 1);
+       // The implicit leading mantissa bit is set iff the number is normal.
+       __glibcxx_assert(((mantissa & (1ull << mantissa_bits)) != 0)
+                        == (biased_exponent != 0));
+      }
+    if constexpr (is_same_v<T, float> || is_same_v<T, double>)
+      memcpy(&value, &result, sizeof(result));
+#if USE_LIB_FAST_FLOAT
+    else if constexpr (is_same_v<T, fast_float::floating_type_bfloat16_t>)
+      {
+       uint32_t res = uint32_t{result} << 16;
+       memcpy(value.x, &res, sizeof(res));
+      }
+    else
+      {
+       // Otherwise float16_t which needs to be converted to float32_t.
+       uint32_t res;
+       if ((result & 0x7FFF) == 0)
+         res = uint32_t{result} << 16;         // +/-0.0f16
+       else if ((result & 0x7C00) == 0)
+         {                                     // denormal
+           unsigned n = (std::numeric_limits<unsigned int>::digits
+                         - __builtin_clz (result & 0x3FF) - 1);
+           res = uint32_t{result} & 0x3FF & ~(uint32_t{1} << n);
+           res <<= 23 - n;
+           res |= (((uint32_t{n} + 0x67) << 23)
+                   | ((uint32_t{result} & 0x8000) << 16));
+         }
+       else
+         res = (((uint32_t{result} & 0x3FF) << 13)
+                | ((((uint32_t{result} >> 10) & 0x1F) + 0x70) << 23)
+                | ((uint32_t{result} & 0x8000) << 16));
+       memcpy(value.x, &res, sizeof(res));
+      }
+#endif
+
+    return {first, errc{}};
+  }
+#endif // _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
+
 } // namespace
 
-// FIXME: This should be reimplemented so it doesn't use strtod and newlocale.
-// That will avoid the need for any memory allocation, meaning that the
-// non-conforming errc::not_enough_memory result cannot happen.
+#if USE_LIB_FAST_FLOAT || USE_STRTOD_FOR_FROM_CHARS
 
 from_chars_result
 from_chars(const char* first, const char* last, float& value,
           chars_format fmt) noexcept
 {
-  buffer_resource mr;
-  pmr::string buf(&mr);
-  size_t len = 0;
-  errc ec = errc::invalid_argument;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-       len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+#if USE_LIB_FAST_FLOAT
+  if (fmt == chars_format::hex)
+    return __floating_from_chars_hex(first, last, value);
+  else
+    return fast_float::from_chars(first, last, value, fmt);
+#else
+  return from_chars_strtod(first, last, value, fmt);
+#endif
 }
 
 from_chars_result
 from_chars(const char* first, const char* last, double& value,
           chars_format fmt) noexcept
 {
-  buffer_resource mr;
-  pmr::string buf(&mr);
-  size_t len = 0;
-  errc ec = errc::invalid_argument;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-       len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+#if USE_LIB_FAST_FLOAT
+  if (fmt == chars_format::hex)
+    return __floating_from_chars_hex(first, last, value);
+  else
+    return fast_float::from_chars(first, last, value, fmt);
+#else
+  return from_chars_strtod(first, last, value, fmt);
+#endif
 }
 
 from_chars_result
 from_chars(const char* first, const char* last, long double& value,
           chars_format fmt) noexcept
 {
-  buffer_resource mr;
-  pmr::string buf(&mr);
-  size_t len = 0;
-  errc ec = errc::invalid_argument;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-       len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+#if ! USE_STRTOD_FOR_FROM_CHARS
+  // Either long double is the same as double, or we can't use strtold.
+  // In the latter case, this might give an incorrect result (e.g. values
+  // out of range of double give an error, even if they fit in long double).
+  double dbl_value;
+  from_chars_result result;
+  if (fmt == chars_format::hex)
+    result = __floating_from_chars_hex(first, last, dbl_value);
+  else
+    result = fast_float::from_chars(first, last, dbl_value, fmt);
+  if (result.ec == errc{})
+    value = dbl_value;
+  return result;
+#else
+  return from_chars_strtod(first, last, value, fmt);
+#endif
+}
+
+#if USE_LIB_FAST_FLOAT
+// Entrypoints for 16-bit floats.
+[[gnu::cold]] from_chars_result
+__from_chars_float16_t(const char* first, const char* last, float& value,
+                      chars_format fmt) noexcept
+{
+  struct fast_float::floating_type_float16_t val{ &value, 0 };
+  if (fmt == chars_format::hex)
+    return __floating_from_chars_hex(first, last, val);
+  else
+    return fast_float::from_chars_16(first, last, val, fmt);
 }
 
+[[gnu::cold]] from_chars_result
+__from_chars_bfloat16_t(const char* first, const char* last, float& value,
+                       chars_format fmt) noexcept
+{
+  struct fast_float::floating_type_bfloat16_t val{ &value, 0 };
+  if (fmt == chars_format::hex)
+    return __floating_from_chars_hex(first, last, val);
+  else
+    return fast_float::from_chars_16(first, last, val, fmt);
+}
+#endif
+
 #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+// Make std::from_chars for 64-bit long double an alias for the overload
+// for double.
 extern "C" from_chars_result
 _ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last,
                                         long double& value,
@@ -433,6 +1260,25 @@ _ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last,
 __attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format")));
 #endif
 
+#ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
+from_chars_result
+from_chars(const char* first, const char* last, __ieee128& 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);
+}
+#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
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
-#endif // _GLIBCXX_HAVE_USELOCALE