fi
 
 # For libtool versioning info, format is CURRENT:REVISION:AGE
-libtool_VERSION=6:28:0
+libtool_VERSION=6:29:0
 
 # Everything parsed; figure out what files and settings to use.
 case $enable_symvers in
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the `uselocale' function. */
+#undef HAVE_USELOCALE
+
 /* Defined if usleep exists. */
 #undef HAVE_USLEEP
 
 /* Define if writev is available in <sys/uio.h>. */
 #undef HAVE_WRITEV
 
+/* Define to 1 if you have the <xlocale.h> header file. */
+#undef HAVE_XLOCALE_H
+
 /* Define to 1 if you have the `_acosf' function. */
 #undef HAVE__ACOSF
 
 
 
 } GLIBCXX_3.4.27;
 
+GLIBCXX_3.4.29 {
+
+    # std::from_chars
+    _ZSt10from_charsPKcS0_R[def]St12chars_format;
+
+} GLIBCXX_3.4.28;
+
 # Symbols in the support library (libsupc++) have their own tag.
 CXXABI_1.3 {
 
 
   __gnu_cxx_ldbl1287num_getI[cw]*16_M_extract_floatB5cxx11*;
 } GLIBCXX_LDBL_3.4.10;
 
+GLIBCXX_LDBL_3.4.29 {
+  _ZSt10from_charsPKcS0_RgSt12chars_format;
+} GLIBCXX_LDBL_3.4.21;
+
 CXXABI_LDBL_1.3 {
   _ZT[IS]g;
   _ZT[IS]Pg;
 
 done
 
 
+for ac_header in xlocale.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "xlocale.h" "ac_cv_header_xlocale_h" "$ac_includes_default"
+if test "x$ac_cv_header_xlocale_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_XLOCALE_H 1
+_ACEOF
+
+fi
+
+done
+
+
 # Only do link tests if native. Else, hardcode.
 if $GLIBCXX_IS_NATIVE; then
 
 #define HAVE_SOCKATMARK 1
 _ACEOF
 
+fi
+done
+
+
+  # Non-standard functions used by C++17 std::from_chars
+  for ac_func in uselocale
+do :
+  ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale"
+if test "x$ac_cv_func_uselocale" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_USELOCALE 1
+_ACEOF
+
 fi
 done
 
 
   fi
 
+
+    $as_echo "#define HAVE_USELOCALE 1" >>confdefs.h
+
     ;;
 
   *-darwin*)
 
   CXXFLAGS="$ac_save_CXXFLAGS"
 
+
+    for ac_func in uselocale
+do :
+  ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale"
+if test "x$ac_cv_func_uselocale" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_USELOCALE 1
+_ACEOF
+
+fi
+done
+
     ;;
 
   *djgpp)
 #define HAVE_SOCKATMARK 1
 _ACEOF
 
+fi
+done
+
+    for ac_func in uselocale
+do :
+  ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale"
+if test "x$ac_cv_func_uselocale" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_USELOCALE 1
+_ACEOF
+
 fi
 done
 
 fi
 done
 
+    for ac_func in uselocale
+do :
+  ac_fn_c_check_func "$LINENO" "uselocale" "ac_cv_func_uselocale"
+if test "x$ac_cv_func_uselocale" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_USELOCALE 1
+_ACEOF
+
+fi
+done
+
 
 
 
 fi
 
 # For libtool versioning info, format is CURRENT:REVISION:AGE
-libtool_VERSION=6:28:0
+libtool_VERSION=6:29:0
 
 # Everything parsed; figure out what files and settings to use.
 case $enable_symvers in
 
 #endif
 ]])
 
+AC_CHECK_HEADERS([xlocale.h])
+
 # Only do link tests if native. Else, hardcode.
 if $GLIBCXX_IS_NATIVE; then
 
   # For Networking TS.
   AC_CHECK_FUNCS(sockatmark)
 
+  # Non-standard functions used by C++17 std::from_chars
+  AC_CHECK_FUNCS(uselocale)
+
   # For iconv support.
   AM_ICONV
 
 
     # We don't yet support AIX's TLS ABI.
     #GCC_CHECK_TLS
     AM_ICONV
+
+    AC_DEFINE(HAVE_USELOCALE)
     ;;
 
   *-darwin*)
     # Don't call GLIBCXX_CHECK_LINKER_FEATURES, Darwin doesn't have a GNU ld
     GLIBCXX_CHECK_MATH_SUPPORT
     GLIBCXX_CHECK_STDLIB_SUPPORT
+
+    AC_CHECK_FUNCS(uselocale)
     ;;
 
   *djgpp)
     AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
     AC_CHECK_FUNCS(timespec_get)
     AC_CHECK_FUNCS(sockatmark)
+    AC_CHECK_FUNCS(uselocale)
     ;;
 
   *-fuchsia*)
     AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
     AC_CHECK_FUNCS(timespec_get)
     AC_CHECK_FUNCS(sockatmark)
+    AC_CHECK_FUNCS(uselocale)
     AM_ICONV
     ;;
   *-mingw32*)
 
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
+#if _GLIBCXX_HAVE_USELOCALE
+  from_chars_result
+  from_chars(const char* __first, const char* __last, float& __value,
+            chars_format __fmt = chars_format::general);
+
+  from_chars_result
+  from_chars(const char* __first, const char* __last, double& __value,
+            chars_format __fmt = chars_format::general);
+
+  from_chars_result
+  from_chars(const char* __first, const char* __last, long double& __value,
+            chars_format __fmt = chars_format::general);
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // C++14
 
 endif
 
 sources = \
+       floating_from_chars.cc \
        fs_dir.cc \
        fs_ops.cc \
        fs_path.cc \
 
 libc__17convenience_la_LIBADD =
 @ENABLE_DUAL_ABI_TRUE@am__objects_1 = cow-fs_dir.lo cow-fs_ops.lo \
 @ENABLE_DUAL_ABI_TRUE@ cow-fs_path.lo
-am__objects_2 = fs_dir.lo fs_ops.lo fs_path.lo memory_resource.lo \
-       $(am__objects_1)
+am__objects_2 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
+       memory_resource.lo $(am__objects_1)
 @ENABLE_DUAL_ABI_TRUE@am__objects_3 = cow-string-inst.lo
 @ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_4 = ostream-inst.lo \
 @ENABLE_EXTERN_TEMPLATE_TRUE@  string-inst.lo $(am__objects_3)
 @ENABLE_EXTERN_TEMPLATE_TRUE@  $(extra_string_inst_sources)
 
 sources = \
+       floating_from_chars.cc \
        fs_dir.cc \
        fs_ops.cc \
        fs_path.cc \
 
--- /dev/null
+// std::from_chars implementation for floating-point types -*- C++ -*-
+
+// Copyright (C) 2020 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+//
+// ISO C++ 14882:2017
+// 23.2.9  Primitive numeric input conversion [utility.from.chars]
+//
+
+#include <charconv>
+#include <string>
+#include <memory_resource>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <locale.h>
+#include <bits/functexcept.h>
+#if _GLIBCXX_HAVE_XLOCALE_H
+# include <xlocale.h>
+#endif
+
+#if _GLIBCXX_HAVE_USELOCALE
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+namespace
+{
+  // 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
+  // a single allocation, so there's no need for anything more complex.
+  struct buffer_resource : pmr::memory_resource
+  {
+    ~buffer_resource() { if (m_ptr) operator delete(m_ptr, m_bytes); }
+
+    void*
+    do_allocate(size_t bytes, size_t alignment [[maybe_unused]]) override
+    {
+      // Allocate from the buffer if it will fit.
+      if (m_bytes < sizeof(m_buf) && (m_bytes + bytes) <= sizeof(m_buf))
+       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;
+      return m_ptr;
+    }
+
+    void
+    do_deallocate(void*, size_t, size_t) noexcept override
+    { /* like pmr::monotonic_buffer_resource, do nothing here */ }
+
+    bool
+    do_is_equal(const pmr::memory_resource& other) const noexcept override
+    { return &other == this; }
+
+    static constexpr int guaranteed_capacity() { return sizeof(m_buf); }
+
+  private:
+    char m_buf[512];
+    size_t m_bytes = 0;
+    void* m_ptr = nullptr;
+  };
+
+  inline bool valid_fmt(chars_format fmt)
+  {
+    return fmt != chars_format{}
+      && ((fmt & chars_format::general) == fmt
+         || (fmt & chars_format::hex) == fmt);
+  }
+
+  constexpr char hex_digits[] = "abcdefABCDEF0123456789";
+  constexpr auto dec_digits = hex_digits + 12;
+
+  // 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'.
+  const char*
+  find_end_of_float(const char* first, const char* last, const char* digits,
+                   char exp)
+  {
+    while (first < last && strchr(digits, *first) != nullptr)
+      ++first;
+    if (first < last && *first == '.')
+      {
+       ++first;
+       while (first < last && strchr(digits, *first))
+         ++first;
+      }
+    if (first < last && exp != 0 && std::tolower((unsigned char)*first) == exp)
+      {
+       ++first;
+       if (first < last && (*first == '-' || *first == '+'))
+         ++first;
+       while (first < last && strchr(dec_digits, *first) != nullptr)
+         ++first;
+      }
+    return first;
+  }
+
+  // Determine the prefix of [first, last) that matches the pattern
+  // corresponding to `fmt`.
+  // Returns a NTBS containing the pattern, using `buf` to allocate
+  // additional storage if needed.
+  // 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)
+  {
+    // fmt has the value of one of the enumerators of chars_format.
+    __glibcxx_assert(valid_fmt(fmt));
+
+    string_view res;
+
+    if (first == last || *first == '+') [[unlikely]]
+       return nullptr;
+
+    const int neg = (*first == '-');
+
+    if (std::memchr("iInN", (unsigned char)first[neg], 4))
+      {
+       ptrdiff_t len = last - first;
+       if (len < (3 + neg))
+         return nullptr;
+
+       // possible infinity or NaN, let strtod decide
+       if (first[neg] == 'i' || first[neg] == 'I')
+         {
+           // Need at most 9 chars for "-INFINITY", ignore anything after it.
+           len = std::min(len, ptrdiff_t(neg + 8));
+         }
+       else if (len > (neg + 3) && first[neg + 3] == '(')
+         {
+           // Look for end of "NAN(n-char-sequence)"
+           if (void* p = std::memchr(const_cast<char*>(first)+4, ')', len-4))
+             len = static_cast<char*>(p) + 1 - first;
+#ifndef __cpp_exceptions
+           if (len > buffer_resource::guaranteed_capacity())
+             {
+               // The character sequence is too large for the buffer.
+               // Allocation failure could terminate the process,
+               // so just return an error via the fmt parameter.
+               fmt = chars_format{};
+               return nullptr;
+             }
+#endif
+         }
+       else // Only need 4 chars for "-NAN"
+         len = neg + 3;
+
+       buf.assign(first, 0, len);
+       // prevent make_result correcting for "0x"
+       fmt = chars_format::general;
+       return buf.c_str();
+      }
+
+    const char* digits;
+    char* ptr;
+
+    // Assign [first,last) to a std::string to get a NTBS that can be used
+    // with strspn, strtod etc.
+    // If the string would be longer than the fixed buffer inside the
+    // buffer_resource type use find_end_of_float to try to reduce how
+    // much memory is needed, to reduce the chance of std::bad_alloc.
+
+    if (fmt == chars_format::hex)
+      {
+       digits = hex_digits;
+
+       if ((last - first + 2) > buffer_resource::guaranteed_capacity())
+         {
+           last = find_end_of_float(first + neg, last, digits, 'p');
+#ifndef __cpp_exceptions
+           if ((last - first + 2) > buffer_resource::guaranteed_capacity())
+             {
+               // The character sequence is still too large for the buffer.
+               // Allocation failure could terminate the process,
+               // so just return an error via the fmt parameter.
+               fmt = chars_format{};
+               return nullptr;
+             }
+#endif
+         }
+
+       buf = "-0x" + !neg;
+       buf.append(first + neg, last);
+       ptr = buf.data() + neg + 2;
+      }
+    else
+      {
+       digits = dec_digits;
+
+       if ((last - first) > buffer_resource::guaranteed_capacity())
+         {
+           last = find_end_of_float(first + neg, last, digits,
+                                    "e"[fmt == chars_format::fixed]);
+#ifndef __cpp_exceptions
+           if ((last - first) > buffer_resource::guaranteed_capacity())
+             {
+               // The character sequence is still too large for the buffer.
+               // Allocation failure could terminate the process,
+               // so just return an error via the fmt parameter.
+               fmt = chars_format{};
+               return nullptr;
+             }
+#endif
+         }
+       buf.assign(first, last);
+       ptr = buf.data() + neg;
+      }
+
+    // "A non-empty sequence of decimal digits" or
+    // "A non-empty sequence of hexadecimal digits"
+    size_t len = std::strspn(ptr, digits);
+    // "possibly containing a radix character,"
+    if (ptr[len] == '.')
+      {
+       const size_t len2 = std::strspn(ptr + len + 1, digits);
+       if (len + len2)
+         ptr += len + 1 + len2;
+       else
+         return nullptr;
+      }
+    else if (len == 0) [[unlikely]]
+      return nullptr;
+    else
+      ptr += len;
+
+    if (fmt == chars_format::fixed)
+      {
+       // Truncate the string to stop strtod parsing past this point.
+       *ptr = '\0';
+      }
+    else if (fmt == chars_format::scientific)
+      {
+       // Check for required exponent part which starts with 'e' or 'E'
+       if (*ptr != 'e' && *ptr != 'E')
+         return nullptr;
+       // then an optional plus or minus sign
+       const int sign = (ptr[1] == '-' || ptr[1] == '+');
+       // then a nonempty sequence of decimal digits
+       if (!std::memchr(dec_digits, (unsigned char)ptr[1+sign], 10))
+         return nullptr;
+      }
+    else if (fmt == chars_format::general)
+      {
+       if (*ptr == 'x' || *ptr == 'X')
+         *ptr = '\0';
+      }
+
+    return buf.c_str();
+  }
+
+  // Convert the NTBS `str` to a floating-point value of type `T`.
+  // If `str` cannot be converted, `value` is unchanged and `0` is returned.
+  // Otherwise, let N be the number of characters consumed from `str`.
+  // On success `value` is set to the converted value and N is returned.
+  // If the converted value is out of range, `value` is unchanged and
+  // -N is returned.
+  template<typename T>
+  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]]
+      {
+       locale_t orig = ::uselocale(loc);
+
+       const int save_errno = errno;
+       errno = 0;
+       char* endptr;
+       T tmpval;
+       if constexpr (is_same_v<T, float>)
+         tmpval = std::strtof(str, &endptr);
+       if constexpr (is_same_v<T, double>)
+         tmpval = std::strtod(str, &endptr);
+       else if constexpr (is_same_v<T, long double>)
+         tmpval = std::strtold(str, &endptr);
+       const int conv_errno = std::__exchange(errno, save_errno);
+
+       ::uselocale(orig);
+       ::freelocale(loc);
+
+       const ptrdiff_t n = endptr - str;
+       if (conv_errno == ERANGE) [[unlikely]]
+         {
+           if (std::isinf(tmpval)) // overflow
+             ec = errc::result_out_of_range;
+           else // underflow (LWG 3081 wants to set value = tmpval here)
+             ec = errc::result_out_of_range;
+         }
+       else if (n)
+         {
+           value = tmpval;
+           ec = errc();
+         }
+       return n;
+      }
+    else if (errno == ENOMEM)
+      ec = errc::not_enough_memory;
+
+    return 0;
+  }
+
+  inline from_chars_result
+  make_result(const char* str, ptrdiff_t n, chars_format fmt, errc ec) noexcept
+  {
+    from_chars_result result = { str, ec };
+    if (n != 0)
+      {
+       if (fmt == chars_format::hex)
+         n -= 2; // correct for the "0x" inserted into the pattern
+       result.ptr += n;
+      }
+    else if (fmt == chars_format{}) [[unlikely]]
+      {
+       // FIXME: the standard does not allow this result.
+       ec = errc::not_enough_memory;
+      }
+    return result;
+  }
+
+} // 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.
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+#ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
+extern "C" from_chars_result
+_ZSt10from_charsPKcS0_ReSt12chars_format(const char* first, const char* last,
+                                        long double& value,
+                                        chars_format fmt) noexcept
+__attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format")));
+#endif
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // _GLIBCXX_HAVE_USELOCALE
 
 }
 
 // { dg-prune-output "enable_if" }
+// { dg-prune-output "cannot bind non-const lvalue reference" }
 
 }
 
 // { dg-prune-output "enable_if" }
+// { dg-prune-output "cannot bind non-const lvalue reference" }
 
   VERIFY( r.ptr == s.data() );
   VERIFY( i == 999 );
 
+  s = "+1";
+  r = std::from_chars(s.data(), s.data() + s.length(), i);
+  VERIFY( r.ec == std::errc::invalid_argument );
+  VERIFY( r.ptr == s.data() );
+  VERIFY( i == 999 );
+
   unsigned u = 888;
   s = "-1";
   r = std::from_chars(s.data(), s.data() + s.length(), u);
   VERIFY( r.ec == std::errc::invalid_argument );
   VERIFY( r.ptr == s.data() );
   VERIFY( u == 888 );
+  s = "+1";
+  r = std::from_chars(s.data(), s.data() + s.length(), u);
+  VERIFY( r.ec == std::errc::invalid_argument );
+  VERIFY( r.ptr == s.data() );
+  VERIFY( u == 888 );
 
   for (int base = 2; base <= 36; ++base)
   {
 
--- /dev/null
+// Copyright (C) 2020 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/>.
+
+// <charconv> is supported in C++14 as a GNU extension
+// { dg-do run { target c++14 } }
+
+#include <charconv>
+#include <string>
+#include <limits>
+#include <cmath>
+#include <cstdlib>
+#include <testsuite_hooks.h>
+
+// Test std::from_chars floating-point conversions.
+
+void
+test01()
+{
+  std::string s;
+  double d;
+  std::from_chars_result res;
+
+  for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific,
+                   std::chars_format::general, std::chars_format::hex })
+  {
+    s = "Info";
+    res = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+    VERIFY( std::isinf(d) );
+    VERIFY( res.ptr == s.data() + 3 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "-INFIN";
+    res = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+    VERIFY( std::isinf(d) );
+    VERIFY( d < 0 );
+    VERIFY( res.ptr == s.data() + 4 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "InFiNiTy aNd BeYoNd";
+    res = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+    VERIFY( std::isinf(d) );
+    VERIFY( res.ptr == s.data() + 8 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "nAn";
+    res = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+    VERIFY( std::isnan(d) );
+    VERIFY( res.ptr == s.data() + 3 );
+    VERIFY( res.ec == std::errc{} );
+
+    s = "-NAN()";
+    res = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+    VERIFY( std::isnan(d) );
+    VERIFY( res.ptr == s.data() + s.length() );
+    VERIFY( res.ec == std::errc{} );
+  }
+}
+
+void
+test02()
+{
+  std::string s;
+  double d = 1.0;
+  std::from_chars_result res;
+
+  s = "0x123";
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( d == 0.0 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::fixed);
+  VERIFY( d == 0.0 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::scientific);
+  VERIFY( d == 1.0 );
+  VERIFY( res.ptr == s.data() );
+  VERIFY( res.ec == std::errc::invalid_argument );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::general);
+  VERIFY( d == 0.0 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::hex);
+  VERIFY( d == 0.0 );
+  VERIFY( res.ptr == s.data() + 1 );
+  VERIFY( res.ec == std::errc{} );
+}
+
+void
+test03()
+{
+  std::string s;
+  double d = 1.0;
+  std::from_chars_result res;
+
+  s = "0.5e+2azzz";
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( d == 0.5e+2 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::fixed);
+  VERIFY( d == 0.5 );
+  VERIFY( res.ptr == s.data() + 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::scientific);
+  VERIFY( d == 0.5e+2 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::general);
+  VERIFY( d == 0.5e+2 );
+  VERIFY( res.ptr == s.data() + s.length() - 1 - 3 );
+  VERIFY( res.ec == std::errc{} );
+
+  d = 1.0;
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::hex);
+  VERIFY( d == 0x0.5Ep0 );
+  VERIFY( res.ptr == s.data() + 4 );
+  VERIFY( res.ec == std::errc{} );
+
+  s = "1.Ap-2zzz";
+  res = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::hex);
+  VERIFY( d == 0.40625 );
+  VERIFY( res.ptr == s.data() + s.length() - 3 );
+  VERIFY( res.ec == std::errc{} );
+}
+
+void
+test04()
+{
+  // Huge input strings
+  std::string s(1000, '0');
+  double d = 1.0;
+  std::from_chars_result res;
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( res.ptr == s.data() + s.length() );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( d == 0.0 );
+
+  s += ".5";
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( res.ptr == s.data() + s.length() );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( d == 0.5 );
+
+  s += "e2";
+  auto len = s.length();
+  s += std::string(1000, 'a');
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( res.ptr == s.data() + len );
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( d == 50 );
+}
+
+using std::to_string;
+
+#ifdef __GLIBCXX_TYPE_INT_N_0
+std::string
+to_string(unsigned __GLIBCXX_TYPE_INT_N_0 val)
+{
+  using Limits = std::numeric_limits<unsigned __GLIBCXX_TYPE_INT_N_0>;
+  std::string s(Limits::digits10+2, '0');
+  for (auto iter = s.end(); val != 0; val /= 10)
+    *--iter = '0' + (val % 10);
+  return s;
+}
+#endif
+
+void
+test05()
+{
+  std::from_chars_result res;
+  float flt;
+  double dbl;
+  long double ldbl;
+
+  // Small integer values that are exactly representable
+
+  for (int i = 0; i < 100; ++i)
+  {
+    std::string s = to_string(i);
+    int len = s.length();
+    s += "123";
+    const char* s1 = s.c_str();
+    const char* s1_end = s1 + len;
+
+    for (auto fmt : { std::chars_format::fixed,
+                     std::chars_format::general,
+                     std::chars_format::hex })
+    {
+      if (fmt == std::chars_format::hex && i > 9)
+       continue;
+
+      res = std::from_chars(s1, s1_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s1_end );
+      VERIFY( flt == i );
+
+      res = std::from_chars(s1, s1_end, dbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s1_end );
+      VERIFY( dbl == i );
+
+      res = std::from_chars(s1, s1_end, ldbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s1_end );
+      VERIFY( ldbl == i );
+    }
+
+    if (i > 9)
+      continue;
+
+    // Test single-digit integers with small exponents.
+
+    const char s2[] = { '.', *s1, 'e', '0', '0', '0', '1' };
+    const char* s2_end = s2 + sizeof(s2);
+
+    const char s3[] = { *s1, '0', 'e', '-', '0', '0', '1' };
+    const char* s3_end = s3 + sizeof(s3);
+
+    for (auto fmt : { std::chars_format::scientific,
+                     std::chars_format::general })
+    {
+      res = std::from_chars(s2, s2_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s2_end );
+      VERIFY( flt == i );
+
+      res = std::from_chars(s3, s3_end, flt, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s3_end );
+      VERIFY( flt == i );
+
+      res = std::from_chars(s2, s2_end, dbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s2_end );
+      VERIFY( dbl == i );
+
+      res = std::from_chars(s3, s3_end, dbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s3_end );
+      VERIFY( dbl == i );
+
+      res = std::from_chars(s2, s2_end, ldbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s2_end );
+      VERIFY( ldbl == i );
+
+      res = std::from_chars(s3, s3_end, ldbl, fmt);
+      VERIFY( res.ec == std::errc{} );
+      VERIFY( res.ptr == s3_end );
+      VERIFY( ldbl == i );
+    }
+  }
+}
+
+template<typename FloatT, typename UIntT>
+void
+test_max_mantissa()
+{
+  using Float_limits = std::numeric_limits<FloatT>;
+  using UInt_limits = std::numeric_limits<UIntT>;
+
+  if constexpr (Float_limits::is_iec559
+               && Float_limits::digits < UInt_limits::digits)
+  {
+    std::printf("Testing %d-bit float, using %zu-bit integer\n",
+       Float_limits::digits + (int)std::log2(Float_limits::max_exponent) + 1,
+       sizeof(UIntT) * __CHAR_BIT__);
+
+    std::from_chars_result res;
+    FloatT flt;
+
+    for (int i = 0; i < 10; ++i)
+    {
+      // (1 << digits) - 1 is the maximum value of the mantissa
+      const auto val = ((UIntT)1 << Float_limits::digits) - 1 - i;
+      std::string s = to_string(val);
+      auto len = s.length();
+      s += "000"; // these should be ignored
+      for (auto fmt : { std::chars_format::fixed,
+                       std::chars_format::general })
+      {
+       res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+       VERIFY( res.ec == std::errc{} );
+       VERIFY( res.ptr == s.data() + len );
+       VERIFY( flt == val );
+      }
+      s.resize(len);
+      const auto orig_len = len;
+      s += "e+000";
+      len = s.length();
+      s += "111";
+      for (auto fmt : { std::chars_format::scientific,
+                       std::chars_format::general })
+      {
+       res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+       VERIFY( res.ec == std::errc{} );
+       VERIFY( res.ptr == s.data() + len );
+       VERIFY( flt == val );
+
+       std::string s2 = s.substr(0, len - 5);
+       s2.insert(s2.cbegin() + orig_len - 1, '.');
+       s2 += "e000000000001";
+       res = std::from_chars(s.data(), s.data() + len, flt, fmt);
+       VERIFY( res.ec == std::errc{} );
+       VERIFY( res.ptr == s.data() + len );
+       VERIFY( flt == val );
+      }
+    }
+  }
+}
+
+void
+test06()
+{
+  test_max_mantissa<float, unsigned long>();
+  test_max_mantissa<double, unsigned long long>();
+#ifdef __GLIBCXX_TYPE_INT_N_0
+  test_max_mantissa<long double, unsigned __GLIBCXX_TYPE_INT_N_0>();
+#endif
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+}
 
--- /dev/null
+// Copyright (C) 2020 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/>.
+
+// <charconv> is supported in C++14 as a GNU extension
+// { dg-do run { target c++14 } }
+
+#include <charconv>
+#include <string>
+#include <cmath>
+#include <testsuite_hooks.h>
+
+// Test std::from_chars error handling.
+
+void
+test01()
+{
+  std::from_chars_result r;
+  double d = 3.2;
+  std::string s;
+
+  for (auto p : { "", "*", ".", "-", "-*", "-.", "+", "+.", "+-", "-+", "+1",
+                 ".p1", "-.p1",
+                 "in", "inch", "+inf", "na", "nam", "+nan" })
+  {
+    s = p;
+    for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific,
+                     std::chars_format::general, std::chars_format::hex })
+    {
+      r = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+      VERIFY( r.ec == std::errc::invalid_argument );
+      VERIFY( r.ptr == s.data() );
+      VERIFY( d == 3.2 );
+    }
+  }
+
+  for (auto p : { ".e1", "-.e1" }) // These are valid patterns for hex format
+  {
+    s = p;
+    for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific,
+                     std::chars_format::general })
+    {
+      r = std::from_chars(s.data(), s.data() + s.length(), d, fmt);
+      VERIFY( r.ec == std::errc::invalid_argument );
+      VERIFY( r.ptr == s.data() );
+      VERIFY( d == 3.2 );
+    }
+  }
+
+  // scientific format requires an exponent
+  for (auto p : { "1.2", "-1.2", "1.2e", "-1.2e", "1.2e-", "-1.2e+" })
+  {
+    s = p;
+    r = std::from_chars(s.data(), s.data() + s.length(), d,
+                       std::chars_format::scientific);
+    VERIFY( r.ec == std::errc::invalid_argument );
+    VERIFY( r.ptr == s.data() );
+    VERIFY( d == 3.2 );
+  }
+
+  // patterns that are invalid without the final character
+  for (auto p : { "1", ".1", "-1", "-.1",
+                 "inf", "-inf", "nan", "-nan" })
+  {
+    s = p;
+    for (auto fmt : { std::chars_format::fixed, std::chars_format::scientific,
+                     std::chars_format::general, std::chars_format::hex })
+    {
+      r = std::from_chars(s.data(), s.data() + s.length() - 1, d, fmt);
+      VERIFY( r.ec == std::errc::invalid_argument );
+      VERIFY( r.ptr == s.data() );
+      VERIFY( d == 3.2 );
+    }
+  }
+}
+
+void
+test02()
+{
+  std::from_chars_result r;
+  std::string s;
+
+  float f = 0.5;
+  // Overflow
+  s = "99999999999999999e999999999999999999";
+  r = std::from_chars(s.data(), s.data() + s.length(), f);
+  VERIFY( r.ec == std::errc::result_out_of_range );
+  VERIFY( r.ptr == s.data() + s.length() );
+  VERIFY( f == 0.5 );
+
+  s += '*';
+  r = std::from_chars(s.data(), s.data() + s.length(), f);
+  VERIFY( r.ec == std::errc::result_out_of_range );
+  VERIFY( r.ptr == s.data() + s.length() - 1 );
+  VERIFY( f == 0.5 );
+
+  s.insert(s.begin(), '-');
+  r = std::from_chars(s.data(), s.data() + s.length(), f);
+  VERIFY( r.ec == std::errc::result_out_of_range );
+  VERIFY( r.ptr == s.data() + s.length() - 1 );
+  VERIFY( f == 0.5 );
+}
+
+void
+test03()
+{
+  double d = 0.5;
+  // Underflow
+  std::string s("-1.2345e-9999zzz");
+  std::from_chars_result res;
+  res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( res.ptr == s.data() + s.length() - 3 );
+  VERIFY( res.ec == std::errc::result_out_of_range );
+  VERIFY( d == 0.5 );
+
+  res = std::from_chars(s.data() + 1, s.data() + s.length(), d);
+  VERIFY( res.ptr == s.data() + s.length() - 3 );
+  VERIFY( res.ec == std::errc::result_out_of_range );
+  VERIFY( d == 0.5 );
+}
+
+void
+test04()
+{
+  std::from_chars_result res;
+  std::string z(2000, '0');
+  // Invalid inputs for scientific format
+  for (const char* s : { "", "1", ".", ".0", ".5", "1e+", "1e+-1" })
+  {
+    for (auto len : { 0, 10, 100, 1000, 2000 })
+    {
+      auto str = z.substr(len) + s;
+      double d = 99.0;
+      res = std::from_chars(str.data(), str.data() + str.length(), d,
+                           std::chars_format::scientific);
+      VERIFY( res.ec == std::errc::invalid_argument );
+      VERIFY( res.ptr == str.data() );
+      VERIFY( d == 99.0 );
+    }
+  }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+}
 
       known_versions.push_back("GLIBCXX_3.4.26");
       known_versions.push_back("GLIBCXX_3.4.27");
       known_versions.push_back("GLIBCXX_3.4.28");
+      known_versions.push_back("GLIBCXX_3.4.29");
+      known_versions.push_back("GLIBCXX_LDBL_3.4.29");
       known_versions.push_back("CXXABI_1.3");
       known_versions.push_back("CXXABI_LDBL_1.3");
       known_versions.push_back("CXXABI_1.3.1");
        test.version_status = symbol::incompatible;
 
       // Check that added symbols are added in the latest pre-release version.
-      bool latestp = (test.version_name == "GLIBCXX_3.4.28"
+      bool latestp = (test.version_name == "GLIBCXX_3.4.29"
+         // XXX remove next line when GLIBCXX_3.4.30 is added and baselines
+         // have been regenerated to include GLIBCXX_LDBL_3.4.29 symbols:
+                    || test.version_name == "GLIBCXX_LDBL_3.4.29"
                     || test.version_name == "CXXABI_1.3.12"
                     || test.version_name == "CXXABI_FLOAT128"
                     || test.version_name == "CXXABI_TM_1");