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 <jwak...@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();
+}

Reply via email to