[committed] libstdc++: Add std::from_chars for floating-point types

Message ID 20200720225233.GA174400@redhat.com
State New
Headers show
Series
  • [committed] libstdc++: Add std::from_chars for floating-point types
Related show

Commit Message

Ian Lance Taylor via Gcc-patches July 20, 2020, 10:52 p.m.
This adds the missing std::from_chars overloads for floating-point
types, as required for C++17 conformance.

The implementation is a hack and not intended to be used in the long
term. Rather than parsing the string directly, this determines the
initial portion of the string that matches the pattern determined by the
chars_format parameter, then creates a NTBS to be parsed by strtod (or
strtold or strtof).

Because creating a NTBS requires allocating memory, but std::from_chars
is noexcept, we need to be careful to minimise allocation. Even after
being careful, allocation failure is still possible, and so a
non-conforming std::no_more_memory error code might be returned.

Because strtod et al depend on the current locale, but std::from_chars
does not, we change the current thread's locale to "C" using newlocale
and uselocale before calling strtod, and restore it afterwards.

Because strtod doesn't have the equivalent of a std::chars_format
parameter, it has to examine the input to determine the format in use,
even though the std::from_chars code has already parsed it once (or
twice for large input strings!)

By replacing the use of strtod we could avoid allocation, avoid changing
locale, and use optimised code paths specific to each std::chars_format
case. We would also get more portable behaviour, rather than depending
on the presence of uselocale, and on any bugs or quirks of the target
libc's strtod. Replacing strtod is a project for a later date.

libstdc++-v3/ChangeLog:

	* acinclude.m4 (libtool_VERSION): Bump version.
	* config.h.in: Regenerate.
	* config/abi/pre/gnu.ver: Add GLIBCXX_3.4.29 version and new
	exports.
	* config/os/gnu-linux/ldbl-extra.ver: Add _GLIBCXX_LDBL_3.4.29
	version and new export.
	* configure: Regenerate.
	* configure.ac: Check for <xlocale.h> and uselocale.
	* crossconfig.m4: Add macro or checks for uselocale.
	* include/std/charconv (from_chars): Declare overloads for
	float, double, and long double.
	* src/c++17/Makefile.am: Add new file.
	* src/c++17/Makefile.in: Regenerate.
	* src/c++17/floating_from_chars.cc: New file.
	(from_chars): Define for float, double, and long double.
	* testsuite/20_util/from_chars/1_c++20_neg.cc: Prune extra
	diagnostics caused by new overloads.
	* testsuite/20_util/from_chars/1_neg.cc: Likewise.
	* testsuite/20_util/from_chars/2.cc: Check leading '+'.
	* testsuite/20_util/from_chars/4.cc: New test.
	* testsuite/20_util/from_chars/5.cc: New test.
	* testsuite/util/testsuite_abi.cc: Add new symbol versions.

Tested x86_64-linux, powerpc64l-linux. Committed to trunk.
commit 932fbc868ad429167a3d4d5625aa9d6dc0b4506b
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jul 20 23:49:27 2020

    libstdc++: Add std::from_chars for floating-point types
    
    This adds the missing std::from_chars overloads for floating-point
    types, as required for C++17 conformance.
    
    The implementation is a hack and not intended to be used in the long
    term. Rather than parsing the string directly, this determines the
    initial portion of the string that matches the pattern determined by the
    chars_format parameter, then creates a NTBS to be parsed by strtod (or
    strtold or strtof).
    
    Because creating a NTBS requires allocating memory, but std::from_chars
    is noexcept, we need to be careful to minimise allocation. Even after
    being careful, allocation failure is still possible, and so a
    non-conforming std::no_more_memory error code might be returned.
    
    Because strtod et al depend on the current locale, but std::from_chars
    does not, we change the current thread's locale to "C" using newlocale
    and uselocale before calling strtod, and restore it afterwards.
    
    Because strtod doesn't have the equivalent of a std::chars_format
    parameter, it has to examine the input to determine the format in use,
    even though the std::from_chars code has already parsed it once (or
    twice for large input strings!)
    
    By replacing the use of strtod we could avoid allocation, avoid changing
    locale, and use optimised code paths specific to each std::chars_format
    case. We would also get more portable behaviour, rather than depending
    on the presence of uselocale, and on any bugs or quirks of the target
    libc's strtod. Replacing strtod is a project for a later date.
    
    libstdc++-v3/ChangeLog:
    
            * acinclude.m4 (libtool_VERSION): Bump version.
            * config.h.in: Regenerate.
            * config/abi/pre/gnu.ver: Add GLIBCXX_3.4.29 version and new
            exports.
            * config/os/gnu-linux/ldbl-extra.ver: Add _GLIBCXX_LDBL_3.4.29
            version and new export.
            * configure: Regenerate.
            * configure.ac: Check for <xlocale.h> and uselocale.
            * crossconfig.m4: Add macro or checks for uselocale.
            * include/std/charconv (from_chars): Declare overloads for
            float, double, and long double.
            * src/c++17/Makefile.am: Add new file.
            * src/c++17/Makefile.in: Regenerate.
            * src/c++17/floating_from_chars.cc: New file.
            (from_chars): Define for float, double, and long double.
            * testsuite/20_util/from_chars/1_c++20_neg.cc: Prune extra
            diagnostics caused by new overloads.
            * testsuite/20_util/from_chars/1_neg.cc: Likewise.
            * testsuite/20_util/from_chars/2.cc: Check leading '+'.
            * testsuite/20_util/from_chars/4.cc: New test.
            * testsuite/20_util/from_chars/5.cc: New test.
            * testsuite/util/testsuite_abi.cc: Add new symbol versions.

Comments

Ian Lance Taylor via Gcc-patches July 21, 2020, 5:56 a.m. | #1
* Jonathan Wakely via Libstdc:

> By replacing the use of strtod we could avoid allocation, avoid changing

> locale, and use optimised code paths specific to each std::chars_format

> case. We would also get more portable behaviour, rather than depending

> on the presence of uselocale, and on any bugs or quirks of the target

> libc's strtod. Replacing strtod is a project for a later date.


glibc already has strtod_l (since glibc 2.1, undocumented, but declared
in <stdlib.h>).

What seems to be missing is a function that takes an explicit buffer
length.  A static reference to the C locale object would be helpful as
well, I assume.

Maybe this is sufficiently clean that we can export this for libstdc++'s
use?  Without repeating the libio mess?

Thanks,
Florian
Ian Lance Taylor via Gcc-patches July 21, 2020, 11:04 a.m. | #2
On 21/07/20 07:56 +0200, Florian Weimer wrote:
>* Jonathan Wakely via Libstdc:

>

>> By replacing the use of strtod we could avoid allocation, avoid changing

>> locale, and use optimised code paths specific to each std::chars_format

>> case. We would also get more portable behaviour, rather than depending

>> on the presence of uselocale, and on any bugs or quirks of the target

>> libc's strtod. Replacing strtod is a project for a later date.

>

>glibc already has strtod_l (since glibc 2.1, undocumented, but declared

>in <stdlib.h>).


Yes, I noticed that in the glibc sources. I decided not to bother
using it because we still need the newlocale and freelocale calls,
which can still potentially allocate memory (although in practice
maybe they don't for the "C" locale?) and because what I committed
should work for any POSIX target.

>What seems to be missing is a function that takes an explicit buffer

>length.  A static reference to the C locale object would be helpful as

>well, I assume.


How expensive is it to do newlocale("C", nullptr), uselocale, and
freelocale?

>Maybe this is sufficiently clean that we can export this for libstdc++'s

>use?  Without repeating the libio mess?


I think we could beat strtod's performance with a handwritten
implementation, so I don't know if it's worth adding glibc extensions
if we would stop using them eventually anyway.

std::from_chars takes an enum that says whether the input is in
hex, scientific or fixed format (or 'general' which is
fixed|scientific). Because strtod determines the format itself, we
need to do some preprocessing before calling strtod, to stop it being
too general.

Some examples where strtod does the wrong thing unless we do extra
work before calling it:

"0x1p01" should always produce the result 0, for any format (because
you pass the hex flag to std::from_chars, it doesn't need a "0x"
prefix, and if one is present it's interpreted as simply "0"). If we
don't truncate the string, strtod produces 2.

"0.8p1" should produce 0.8 for fixed and general formats, produce an
error for scientific format, and produce 1 for hex format (which means
we need to create the string "0x0.8p1" to pass to strtod). strtod
always produces 0.8 for this input.

I also noticed some strings give an underflow error with glibc's
strtod, but are valid for the Microsoft implementation. For example,
this one:
https://github.com/microsoft/STL/blob/master/tests/std/tests/P0067R5_charconv/double_from_chars_test_cases.hpp#L265

Without the final '1' digit glibc returns DBL_MIN, but with the final
'1' digit (so a number larger than DBL_MIN) it underflows. Is that
expected?
Joseph Myers July 21, 2020, 8:08 p.m. | #3
On Tue, 21 Jul 2020, Jonathan Wakely via Gcc-patches wrote:

> I also noticed some strings give an underflow error with glibc's

> strtod, but are valid for the Microsoft implementation. For example,

> this one:

> https://github.com/microsoft/STL/blob/master/tests/std/tests/P0067R5_charconv/double_from_chars_test_cases.hpp#L265

> 

> Without the final '1' digit glibc returns DBL_MIN, but with the final

> '1' digit (so a number larger than DBL_MIN) it underflows. Is that

> expected?


That's DBL_TRUE_MIN, not DBL_MIN.  The IEEE rule is that, with default 
exception handling, an exact subnormal result does not raise the underflow 
exception flag, whereas an inexact tiny result raises both inexact and 
underflow flags; glibc mostly doesn't try to ensure an (exact) underflow 
exception is signaled for the case of exact underflow with traps on that 
exception enabled, only correct flags raised with default exception 
handling.

(The way tininess is determined depends on the architecture.  glibc strtod 
handles both cases, before-rounding and after-rounding architectures, 
modulo oddities where implementations of some CPU architectures don't 
appear to be consistent in their before-rounding / after-rounding choice.  
Note that on after-rounding architectures it depends on the result after 
rounding with normal precision but unbounded exponent range.  One 
consequence of that is that the bound on the number of digits after the 
decimal point that may need to be considered, beyond just knowing whether 
any of them are nonzero, to determine the correctly rounded result and 
exceptions, is e.g. 1076 for binary64, not 1075; that is, two digits are 
needed beyond those needed for an exact representation of the least 
subnormal value, although only one such digit is needed for the correctly 
rounded result if you ignore after-rounding tininess detection.)

-- 
Joseph S. Myers
joseph@codesourcery.com
Ian Lance Taylor via Gcc-patches July 22, 2020, 3:09 p.m. | #4
On 21/07/20 20:08 +0000, Joseph Myers wrote:
>On Tue, 21 Jul 2020, Jonathan Wakely via Gcc-patches wrote:

>

>> I also noticed some strings give an underflow error with glibc's

>> strtod, but are valid for the Microsoft implementation. For example,

>> this one:

>> https://github.com/microsoft/STL/blob/master/tests/std/tests/P0067R5_charconv/double_from_chars_test_cases.hpp#L265

>>

>> Without the final '1' digit glibc returns DBL_MIN, but with the final

>> '1' digit (so a number larger than DBL_MIN) it underflows. Is that

>> expected?

>

>That's DBL_TRUE_MIN, not DBL_MIN.  The IEEE rule is that, with default

>exception handling, an exact subnormal result does not raise the underflow

>exception flag, whereas an inexact tiny result raises both inexact and

>underflow flags; glibc mostly doesn't try to ensure an (exact) underflow

>exception is signaled for the case of exact underflow with traps on that

>exception enabled, only correct flags raised with default exception

>handling.

>

>(The way tininess is determined depends on the architecture.  glibc strtod

>handles both cases, before-rounding and after-rounding architectures,

>modulo oddities where implementations of some CPU architectures don't

>appear to be consistent in their before-rounding / after-rounding choice.

>Note that on after-rounding architectures it depends on the result after

>rounding with normal precision but unbounded exponent range.  One

>consequence of that is that the bound on the number of digits after the

>decimal point that may need to be considered, beyond just knowing whether

>any of them are nonzero, to determine the correctly rounded result and

>exceptions, is e.g. 1076 for binary64, not 1075; that is, two digits are

>needed beyond those needed for an exact representation of the least

>subnormal value, although only one such digit is needed for the correctly

>rounded result if you ignore after-rounding tininess detection.)


Aha, thanks very much for the explanation.
Rainer Orth July 27, 2020, 9:41 a.m. | #5
Hi Jonathan,

> This adds the missing std::from_chars overloads for floating-point

> types, as required for C++17 conformance.

>

> The implementation is a hack and not intended to be used in the long

> term. Rather than parsing the string directly, this determines the

> initial portion of the string that matches the pattern determined by the

> chars_format parameter, then creates a NTBS to be parsed by strtod (or

> strtold or strtof).

>

> Because creating a NTBS requires allocating memory, but std::from_chars

> is noexcept, we need to be careful to minimise allocation. Even after

> being careful, allocation failure is still possible, and so a

> non-conforming std::no_more_memory error code might be returned.

>

> Because strtod et al depend on the current locale, but std::from_chars

> does not, we change the current thread's locale to "C" using newlocale

> and uselocale before calling strtod, and restore it afterwards.

>

> Because strtod doesn't have the equivalent of a std::chars_format

> parameter, it has to examine the input to determine the format in use,

> even though the std::from_chars code has already parsed it once (or

> twice for large input strings!)

>

> By replacing the use of strtod we could avoid allocation, avoid changing

> locale, and use optimised code paths specific to each std::chars_format

> case. We would also get more portable behaviour, rather than depending

> on the presence of uselocale, and on any bugs or quirks of the target

> libc's strtod. Replacing strtod is a project for a later date.


two of the new tests FAIL on Solaris 11.3 only:

+FAIL: 20_util/from_chars/4.cc (test for excess errors)
+UNRESOLVED: 20_util/from_chars/4.cc compilation failed to produce executable
+FAIL: 20_util/from_chars/5.cc (test for excess errors)
+UNRESOLVED: 20_util/from_chars/5.cc compilation failed to produce executable

Excess errors:
/vol/gcc/src/hg/master/local/libstdc++-v3/testsuite/20_util/from_chars/4.cc:41: error: no matching function for call to 'from_chars(char*, char*, double&, std::chars_format&)'
[...]

AFAICT that's due to the fact that Solaris 11.3 (unlike 11.4) lacks
uselocale.

	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University
Ian Lance Taylor via Gcc-patches July 27, 2020, 9:46 a.m. | #6
On 27/07/20 11:41 +0200, Rainer Orth wrote:
>Hi Jonathan,

>

>> This adds the missing std::from_chars overloads for floating-point

>> types, as required for C++17 conformance.

>>

>> The implementation is a hack and not intended to be used in the long

>> term. Rather than parsing the string directly, this determines the

>> initial portion of the string that matches the pattern determined by the

>> chars_format parameter, then creates a NTBS to be parsed by strtod (or

>> strtold or strtof).

>>

>> Because creating a NTBS requires allocating memory, but std::from_chars

>> is noexcept, we need to be careful to minimise allocation. Even after

>> being careful, allocation failure is still possible, and so a

>> non-conforming std::no_more_memory error code might be returned.

>>

>> Because strtod et al depend on the current locale, but std::from_chars

>> does not, we change the current thread's locale to "C" using newlocale

>> and uselocale before calling strtod, and restore it afterwards.

>>

>> Because strtod doesn't have the equivalent of a std::chars_format

>> parameter, it has to examine the input to determine the format in use,

>> even though the std::from_chars code has already parsed it once (or

>> twice for large input strings!)

>>

>> By replacing the use of strtod we could avoid allocation, avoid changing

>> locale, and use optimised code paths specific to each std::chars_format

>> case. We would also get more portable behaviour, rather than depending

>> on the presence of uselocale, and on any bugs or quirks of the target

>> libc's strtod. Replacing strtod is a project for a later date.

>

>two of the new tests FAIL on Solaris 11.3 only:

>

>+FAIL: 20_util/from_chars/4.cc (test for excess errors)

>+UNRESOLVED: 20_util/from_chars/4.cc compilation failed to produce executable

>+FAIL: 20_util/from_chars/5.cc (test for excess errors)

>+UNRESOLVED: 20_util/from_chars/5.cc compilation failed to produce executable

>

>Excess errors:

>/vol/gcc/src/hg/master/local/libstdc++-v3/testsuite/20_util/from_chars/4.cc:41: error: no matching function for call to 'from_chars(char*, char*, double&, std::chars_format&)'

>[...]

>

>AFAICT that's due to the fact that Solaris 11.3 (unlike 11.4) lacks

>uselocale.


Ah yes, the tests should not run unconditionally. I'll fix that today,
thanks.
Ian Lance Taylor via Gcc-patches July 28, 2020, 9:01 p.m. | #7
On 27/07/20 10:46 +0100, Jonathan Wakely wrote:
>On 27/07/20 11:41 +0200, Rainer Orth wrote:

>>Hi Jonathan,

>>

>>>This adds the missing std::from_chars overloads for floating-point

>>>types, as required for C++17 conformance.

>>>

>>>The implementation is a hack and not intended to be used in the long

>>>term. Rather than parsing the string directly, this determines the

>>>initial portion of the string that matches the pattern determined by the

>>>chars_format parameter, then creates a NTBS to be parsed by strtod (or

>>>strtold or strtof).

>>>

>>>Because creating a NTBS requires allocating memory, but std::from_chars

>>>is noexcept, we need to be careful to minimise allocation. Even after

>>>being careful, allocation failure is still possible, and so a

>>>non-conforming std::no_more_memory error code might be returned.

>>>

>>>Because strtod et al depend on the current locale, but std::from_chars

>>>does not, we change the current thread's locale to "C" using newlocale

>>>and uselocale before calling strtod, and restore it afterwards.

>>>

>>>Because strtod doesn't have the equivalent of a std::chars_format

>>>parameter, it has to examine the input to determine the format in use,

>>>even though the std::from_chars code has already parsed it once (or

>>>twice for large input strings!)

>>>

>>>By replacing the use of strtod we could avoid allocation, avoid changing

>>>locale, and use optimised code paths specific to each std::chars_format

>>>case. We would also get more portable behaviour, rather than depending

>>>on the presence of uselocale, and on any bugs or quirks of the target

>>>libc's strtod. Replacing strtod is a project for a later date.

>>

>>two of the new tests FAIL on Solaris 11.3 only:

>>

>>+FAIL: 20_util/from_chars/4.cc (test for excess errors)

>>+UNRESOLVED: 20_util/from_chars/4.cc compilation failed to produce executable

>>+FAIL: 20_util/from_chars/5.cc (test for excess errors)

>>+UNRESOLVED: 20_util/from_chars/5.cc compilation failed to produce executable

>>

>>Excess errors:

>>/vol/gcc/src/hg/master/local/libstdc++-v3/testsuite/20_util/from_chars/4.cc:41: error: no matching function for call to 'from_chars(char*, char*, double&, std::chars_format&)'

>>[...]

>>

>>AFAICT that's due to the fact that Solaris 11.3 (unlike 11.4) lacks

>>uselocale.

>

>Ah yes, the tests should not run unconditionally. I'll fix that today,

>thanks.


This should be fixed at 2251b4a5423efa8ee0d7e67537b63e404a1f6afa.
Ian Lance Taylor via Gcc-patches July 28, 2020, 9:02 p.m. | #8
On 28/07/20 22:01 +0100, Jonathan Wakely wrote:
>On 27/07/20 10:46 +0100, Jonathan Wakely wrote:

>>On 27/07/20 11:41 +0200, Rainer Orth wrote:

>>>Hi Jonathan,

>>>

>>>>This adds the missing std::from_chars overloads for floating-point

>>>>types, as required for C++17 conformance.

>>>>

>>>>The implementation is a hack and not intended to be used in the long

>>>>term. Rather than parsing the string directly, this determines the

>>>>initial portion of the string that matches the pattern determined by the

>>>>chars_format parameter, then creates a NTBS to be parsed by strtod (or

>>>>strtold or strtof).

>>>>

>>>>Because creating a NTBS requires allocating memory, but std::from_chars

>>>>is noexcept, we need to be careful to minimise allocation. Even after

>>>>being careful, allocation failure is still possible, and so a

>>>>non-conforming std::no_more_memory error code might be returned.

>>>>

>>>>Because strtod et al depend on the current locale, but std::from_chars

>>>>does not, we change the current thread's locale to "C" using newlocale

>>>>and uselocale before calling strtod, and restore it afterwards.

>>>>

>>>>Because strtod doesn't have the equivalent of a std::chars_format

>>>>parameter, it has to examine the input to determine the format in use,

>>>>even though the std::from_chars code has already parsed it once (or

>>>>twice for large input strings!)

>>>>

>>>>By replacing the use of strtod we could avoid allocation, avoid changing

>>>>locale, and use optimised code paths specific to each std::chars_format

>>>>case. We would also get more portable behaviour, rather than depending

>>>>on the presence of uselocale, and on any bugs or quirks of the target

>>>>libc's strtod. Replacing strtod is a project for a later date.

>>>

>>>two of the new tests FAIL on Solaris 11.3 only:

>>>

>>>+FAIL: 20_util/from_chars/4.cc (test for excess errors)

>>>+UNRESOLVED: 20_util/from_chars/4.cc compilation failed to produce executable

>>>+FAIL: 20_util/from_chars/5.cc (test for excess errors)

>>>+UNRESOLVED: 20_util/from_chars/5.cc compilation failed to produce executable

>>>

>>>Excess errors:

>>>/vol/gcc/src/hg/master/local/libstdc++-v3/testsuite/20_util/from_chars/4.cc:41: error: no matching function for call to 'from_chars(char*, char*, double&, std::chars_format&)'

>>>[...]

>>>

>>>AFAICT that's due to the fact that Solaris 11.3 (unlike 11.4) lacks

>>>uselocale.

>>

>>Ah yes, the tests should not run unconditionally. I'll fix that today,

>>thanks.

>

>This should be fixed at 2251b4a5423efa8ee0d7e67537b63e404a1f6afa.


Oh it looks like I forgot to send that patch to the lists, so here it
is. Tested powerpc64le-linux, committed to trunk.
commit 2251b4a5423efa8ee0d7e67537b63e404a1f6afa
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Mon Jul 27 15:51:16 2020

    libstdc++: Make std::from_chars always round to nearest
    
    Also fix the tests that fail on targets without uselocale.
    
    libstdc++-v3/ChangeLog:
    
            * src/c++17/floating_from_chars.cc (from_chars_impl): Ensure
            that FE_NEAREST is used.
            * testsuite/20_util/from_chars/4.cc: Do not use if constexpr in
            a { target c++14 } test.
            [!_GLIBCXX_HAVE_USELOCALE]: Disable all tests.
            * testsuite/20_util/from_chars/5.cc [!_GLIBCXX_HAVE_USELOCALE]:
            Likewise.
            * testsuite/20_util/from_chars/6.cc: New test.

diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc
index 45de2be283d..f1519e5c7b6 100644
--- a/libstdc++-v3/src/c++17/floating_from_chars.cc
+++ b/libstdc++-v3/src/c++17/floating_from_chars.cc
@@ -30,6 +30,7 @@
 #include <charconv>
 #include <string>
 #include <memory_resource>
+#include <cfenv>
 #include <cmath>
 #include <cstdlib>
 #include <cstring>
@@ -289,6 +290,12 @@ namespace
       {
 	locale_t orig = ::uselocale(loc);
 
+#if _GLIBCXX_USE_C99_FENV_TR1
+	const int rounding = std::fegetround();
+	if (rounding != FE_TONEAREST)
+	  std::fesetround(FE_TONEAREST);
+#endif
+
 	const int save_errno = errno;
 	errno = 0;
 	char* endptr;
@@ -301,6 +308,11 @@ namespace
 	  tmpval = std::strtold(str, &endptr);
 	const int conv_errno = std::__exchange(errno, save_errno);
 
+#if _GLIBCXX_USE_C99_FENV_TR1
+	if (rounding != FE_TONEAREST)
+	  std::fesetround(rounding);
+#endif
+
 	::uselocale(orig);
 	::freelocale(loc);
 
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/4.cc b/libstdc++-v3/testsuite/20_util/from_chars/4.cc
index 6d692592e95..e7127ed0c48 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/4.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/4.cc
@@ -27,6 +27,9 @@
 
 // Test std::from_chars floating-point conversions.
 
+// As of July 2020 __cpp_lib_to_chars is not defined, but std::from_chars
+// works for floating-point types when _GLIBCXX_HAVE_USELOCALE is defined.
+#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE
 void
 test01()
 {
@@ -296,8 +299,7 @@ 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)
+  if (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,
@@ -355,14 +357,17 @@ test06()
   test_max_mantissa<long double, unsigned __GLIBCXX_TYPE_INT_N_0>();
 #endif
 }
+#endif
 
 int
 main()
 {
+#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE
   test01();
   test02();
   test03();
   test04();
   test05();
   test06();
+#endif
 }
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/5.cc b/libstdc++-v3/testsuite/20_util/from_chars/5.cc
index f8fc7f6cd12..9525da8aebe 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/5.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/5.cc
@@ -25,6 +25,9 @@
 
 // Test std::from_chars error handling.
 
+// As of July 2020 __cpp_lib_to_chars is not defined, but std::from_chars
+// works for floating-point types when _GLIBCXX_HAVE_USELOCALE is defined.
+#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE
 void
 test01()
 {
@@ -152,12 +155,15 @@ test04()
     }
   }
 }
+#endif
 
 int
 main()
 {
+#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE
   test01();
   test02();
   test03();
   test04();
+#endif
 }
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/6.cc b/libstdc++-v3/testsuite/20_util/from_chars/6.cc
new file mode 100644
index 00000000000..e592b2eb806
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/from_chars/6.cc
@@ -0,0 +1,49 @@
+// 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 } }
+// { dg-add-options ieee }
+
+#include <charconv>
+#include <string>
+#include <cfenv>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+#if __cpp_lib_to_chars >= 201611L || _GLIBCXX_HAVE_USELOCALE
+#if _GLIBCXX_USE_C99_FENV_TR1
+  double d;
+  std::fesetround(FE_DOWNWARD);
+  const std::string s = "0.099999999999999999999999999";
+  auto res = std::from_chars(s.data(), s.data() + s.length(), d);
+  VERIFY( res.ec == std::errc{} );
+  VERIFY( res.ptr == s.data() + s.length() );
+  // std::from_chars should ignore the current rounding mode
+  // and always round to nearest.
+  VERIFY( d == 0.1 );
+#endif
+#endif
+}
+
+int
+main()
+{
+  test01();
+}
Ian Lance Taylor via Gcc-patches Aug. 3, 2020, 11:47 a.m. | #9
* Jonathan Wakely:

>>What seems to be missing is a function that takes an explicit buffer

>>length.  A static reference to the C locale object would be helpful as

>>well, I assume.

>

> How expensive is it to do newlocale("C", nullptr), uselocale, and

> freelocale?


freelocale does nothing in this situation.  newlocale has a bunch of
conditional branches to detect this situation.  uselocale has fewer
branches, but has more memory accesses.

At least there's no locking involved.  (But I believe uselocale is
thread-unsafe regarding setlocale in the glibc implementation or
something like that.)

>>Maybe this is sufficiently clean that we can export this for libstdc++'s

>>use?  Without repeating the libio mess?

>

> I think we could beat strtod's performance with a handwritten

> implementation, so I don't know if it's worth adding glibc extensions

> if we would stop using them eventually anyway.


I was reminded of this query:

  Robustly parse string to unsigned integer (strtoul?)/docs
  <https://sourceware.org/pipermail/libc-help/2020-June/005337.html>

It suggests to me that we need better interfaces.

> I also noticed some strings give an underflow error with glibc's

> strtod, but are valid for the Microsoft implementation. For example,

> this one:

> https://github.com/microsoft/STL/blob/master/tests/std/tests/P0067R5_charconv/double_from_chars_test_cases.hpp#L265

>

> Without the final '1' digit glibc returns DBL_MIN, but with the final

> '1' digit (so a number larger than DBL_MIN) it underflows. Is that

> expected?


I don't know.  I think you should bring this up on libc-alpha.

Thanks,
Florian
Ian Lance Taylor via Gcc-patches Aug. 3, 2020, 12:30 p.m. | #10
On 03/08/20 13:47 +0200, Florian Weimer wrote:
>* Jonathan Wakely:

>

>>>What seems to be missing is a function that takes an explicit buffer

>>>length.  A static reference to the C locale object would be helpful as

>>>well, I assume.

>>

>> How expensive is it to do newlocale("C", nullptr), uselocale, and

>> freelocale?

>

>freelocale does nothing in this situation.  newlocale has a bunch of

>conditional branches to detect this situation.  uselocale has fewer

>branches, but has more memory accesses.

>

>At least there's no locking involved.  (But I believe uselocale is

>thread-unsafe regarding setlocale in the glibc implementation or

>something like that.)

>

>>>Maybe this is sufficiently clean that we can export this for libstdc++'s

>>>use?  Without repeating the libio mess?

>>

>> I think we could beat strtod's performance with a handwritten

>> implementation, so I don't know if it's worth adding glibc extensions

>> if we would stop using them eventually anyway.

>

>I was reminded of this query:

>

>  Robustly parse string to unsigned integer (strtoul?)/docs

>  <https://sourceware.org/pipermail/libc-help/2020-June/005337.html>

>

>It suggests to me that we need better interfaces.


Yes, the ISO C interfaces for this are poor.

Their error reporting is tricky. You have to check a combination of
the return value, the endptr argument, and errno.
https://movementarian.org/blog/posts/2009-03-14-its-not-just-atol-nicholas/

They are too general. strtod is a single function that parses three
different number formats, with no way to restrict it only one of those
formats.

They require null-terminated strings, making them unsuitable for
certain uses e.g. a byte sequence containing several Pascal-style
length-prefixed strings like "\x05123.4\x351".

They're locale-dependent, which is not useful for machine-readable I/O
where the format of numbers must not be localized e.g. JSON.

And strtoul accepts negative numbers (srsly?!)

C++ has decided to ignore these C functions and has defined better
ones. The fact that libstdc++ currently uses strtod internally is a
temporary kluge.

Replicating the C++ API in glibc might be useful for C devs, but to be
useful for libstdc++ we'd need to copy the code into libstdc++ itself
so we could use it for targets using musl, newlib, BSD, mingw etc.

>> I also noticed some strings give an underflow error with glibc's

>> strtod, but are valid for the Microsoft implementation. For example,

>> this one:

>> https://github.com/microsoft/STL/blob/master/tests/std/tests/P0067R5_charconv/double_from_chars_test_cases.hpp#L265

>>

>> Without the final '1' digit glibc returns DBL_MIN, but with the final

>> '1' digit (so a number larger than DBL_MIN) it underflows. Is that

>> expected?

>

>I don't know.  I think you should bring this up on libc-alpha.


Joseph answered this part, clarifying that glibc is correct to report
underflow.

Patch

diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
index ee5e0336f2c..e3926e1c9c2 100644
--- a/libstdc++-v3/acinclude.m4
+++ b/libstdc++-v3/acinclude.m4
@@ -3846,7 +3846,7 @@  changequote([,])dnl
 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
diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
index edf4485e607..17aff5d907b 100644
--- a/libstdc++-v3/config/abi/pre/gnu.ver
+++ b/libstdc++-v3/config/abi/pre/gnu.ver
@@ -2299,6 +2299,13 @@  GLIBCXX_3.4.28 {
 
 } 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 {
 
diff --git a/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver b/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver
index 5ef4a6cb6e1..b4f3af0f9d9 100644
--- a/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver
+++ b/libstdc++-v3/config/os/gnu-linux/ldbl-extra.ver
@@ -40,6 +40,10 @@  GLIBCXX_LDBL_3.4.21 {
   __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;
diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac
index ffd0079613f..cbfdf4c6bad 100644
--- a/libstdc++-v3/configure.ac
+++ b/libstdc++-v3/configure.ac
@@ -256,6 +256,8 @@  AC_CHECK_HEADERS([linux/random.h], [], [],
 #endif
 ]])
 
+AC_CHECK_HEADERS([xlocale.h])
+
 # Only do link tests if native. Else, hardcode.
 if $GLIBCXX_IS_NATIVE; then
 
@@ -282,6 +284,9 @@  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
 
diff --git a/libstdc++-v3/crossconfig.m4 b/libstdc++-v3/crossconfig.m4
index 313f84d05f4..9f2589b739e 100644
--- a/libstdc++-v3/crossconfig.m4
+++ b/libstdc++-v3/crossconfig.m4
@@ -63,6 +63,8 @@  case "${host}" in
     # We don't yet support AIX's TLS ABI.
     #GCC_CHECK_TLS
     AM_ICONV
+
+    AC_DEFINE(HAVE_USELOCALE)
     ;;
 
   *-darwin*)
@@ -73,6 +75,8 @@  case "${host}" in
     # 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)
@@ -129,6 +133,7 @@  case "${host}" in
     AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
     AC_CHECK_FUNCS(timespec_get)
     AC_CHECK_FUNCS(sockatmark)
+    AC_CHECK_FUNCS(uselocale)
     ;;
 
   *-fuchsia*)
@@ -190,6 +195,7 @@  case "${host}" in
     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*)
diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index cc7dd0e3758..be668c1939e 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -688,6 +688,20 @@  namespace __detail
   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
diff --git a/libstdc++-v3/src/c++17/Makefile.am b/libstdc++-v3/src/c++17/Makefile.am
index 85e31fbd91f..642efb976ac 100644
--- a/libstdc++-v3/src/c++17/Makefile.am
+++ b/libstdc++-v3/src/c++17/Makefile.am
@@ -50,6 +50,7 @@  inst_sources =
 endif
 
 sources = \
+	floating_from_chars.cc \
 	fs_dir.cc \
 	fs_ops.cc \
 	fs_path.cc \
diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc
new file mode 100644
index 00000000000..45de2be283d
--- /dev/null
+++ b/libstdc++-v3/src/c++17/floating_from_chars.cc
@@ -0,0 +1,422 @@ 
+// 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
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc b/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc
index 8454b304d13..b3ca6525681 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/1_c++20_neg.cc
@@ -41,3 +41,4 @@  test01(const char* first, const char* last)
 }
 
 // { dg-prune-output "enable_if" }
+// { dg-prune-output "cannot bind non-const lvalue reference" }
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc b/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc
index 12b5e597b9f..0d2fe2b3e65 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/1_neg.cc
@@ -44,3 +44,4 @@  test01(const char* first, const char* last)
 }
 
 // { dg-prune-output "enable_if" }
+// { dg-prune-output "cannot bind non-const lvalue reference" }
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/2.cc b/libstdc++-v3/testsuite/20_util/from_chars/2.cc
index 902092fd423..e5e2db82c3f 100644
--- a/libstdc++-v3/testsuite/20_util/from_chars/2.cc
+++ b/libstdc++-v3/testsuite/20_util/from_chars/2.cc
@@ -55,6 +55,12 @@  test01()
   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);
@@ -69,6 +75,11 @@  test01()
   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)
   {
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/4.cc b/libstdc++-v3/testsuite/20_util/from_chars/4.cc
new file mode 100644
index 00000000000..6d692592e95
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/from_chars/4.cc
@@ -0,0 +1,368 @@ 
+// 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();
+}
diff --git a/libstdc++-v3/testsuite/20_util/from_chars/5.cc b/libstdc++-v3/testsuite/20_util/from_chars/5.cc
new file mode 100644
index 00000000000..f8fc7f6cd12
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/from_chars/5.cc
@@ -0,0 +1,163 @@ 
+// 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();
+}
diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
index aedb6561ed4..fd8224b6789 100644
--- a/libstdc++-v3/testsuite/util/testsuite_abi.cc
+++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc
@@ -209,6 +209,8 @@  check_version(symbol& test, bool added)
       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");
@@ -240,7 +242,10 @@  check_version(symbol& test, bool added)
 	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");