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.
This commit is contained in:
parent
e443d82138
commit
932fbc868a
|
@ -3846,7 +3846,7 @@ changequote([,])dnl
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For libtool versioning info, format is CURRENT:REVISION:AGE
|
# 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.
|
# Everything parsed; figure out what files and settings to use.
|
||||||
case $enable_symvers in
|
case $enable_symvers in
|
||||||
|
|
|
@ -526,6 +526,9 @@
|
||||||
/* Define to 1 if you have the <unistd.h> header file. */
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
#undef HAVE_UNISTD_H
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `uselocale' function. */
|
||||||
|
#undef HAVE_USELOCALE
|
||||||
|
|
||||||
/* Defined if usleep exists. */
|
/* Defined if usleep exists. */
|
||||||
#undef HAVE_USLEEP
|
#undef HAVE_USLEEP
|
||||||
|
|
||||||
|
@ -556,6 +559,9 @@
|
||||||
/* Define if writev is available in <sys/uio.h>. */
|
/* Define if writev is available in <sys/uio.h>. */
|
||||||
#undef HAVE_WRITEV
|
#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. */
|
/* Define to 1 if you have the `_acosf' function. */
|
||||||
#undef HAVE__ACOSF
|
#undef HAVE__ACOSF
|
||||||
|
|
||||||
|
|
|
@ -2299,6 +2299,13 @@ GLIBCXX_3.4.28 {
|
||||||
|
|
||||||
} GLIBCXX_3.4.27;
|
} 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.
|
# Symbols in the support library (libsupc++) have their own tag.
|
||||||
CXXABI_1.3 {
|
CXXABI_1.3 {
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,10 @@ GLIBCXX_LDBL_3.4.21 {
|
||||||
__gnu_cxx_ldbl1287num_getI[cw]*16_M_extract_floatB5cxx11*;
|
__gnu_cxx_ldbl1287num_getI[cw]*16_M_extract_floatB5cxx11*;
|
||||||
} GLIBCXX_LDBL_3.4.10;
|
} GLIBCXX_LDBL_3.4.10;
|
||||||
|
|
||||||
|
GLIBCXX_LDBL_3.4.29 {
|
||||||
|
_ZSt10from_charsPKcS0_RgSt12chars_format;
|
||||||
|
} GLIBCXX_LDBL_3.4.21;
|
||||||
|
|
||||||
CXXABI_LDBL_1.3 {
|
CXXABI_LDBL_1.3 {
|
||||||
_ZT[IS]g;
|
_ZT[IS]g;
|
||||||
_ZT[IS]Pg;
|
_ZT[IS]Pg;
|
||||||
|
|
|
@ -22661,6 +22661,19 @@ fi
|
||||||
done
|
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.
|
# Only do link tests if native. Else, hardcode.
|
||||||
if $GLIBCXX_IS_NATIVE; then
|
if $GLIBCXX_IS_NATIVE; then
|
||||||
|
|
||||||
|
@ -28986,6 +28999,19 @@ if test "x$ac_cv_func_sockatmark" = xyes; then :
|
||||||
#define HAVE_SOCKATMARK 1
|
#define HAVE_SOCKATMARK 1
|
||||||
_ACEOF
|
_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
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -41997,6 +42023,9 @@ _ACEOF
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
$as_echo "#define HAVE_USELOCALE 1" >>confdefs.h
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*-darwin*)
|
*-darwin*)
|
||||||
|
@ -47770,6 +47799,18 @@ done
|
||||||
|
|
||||||
CXXFLAGS="$ac_save_CXXFLAGS"
|
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)
|
*djgpp)
|
||||||
|
@ -48041,6 +48082,17 @@ if test "x$ac_cv_func_sockatmark" = xyes; then :
|
||||||
#define HAVE_SOCKATMARK 1
|
#define HAVE_SOCKATMARK 1
|
||||||
_ACEOF
|
_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
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -54682,6 +54734,17 @@ _ACEOF
|
||||||
fi
|
fi
|
||||||
done
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -75231,7 +75294,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For libtool versioning info, format is CURRENT:REVISION:AGE
|
# 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.
|
# Everything parsed; figure out what files and settings to use.
|
||||||
case $enable_symvers in
|
case $enable_symvers in
|
||||||
|
|
|
@ -256,6 +256,8 @@ AC_CHECK_HEADERS([linux/random.h], [], [],
|
||||||
#endif
|
#endif
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
AC_CHECK_HEADERS([xlocale.h])
|
||||||
|
|
||||||
# Only do link tests if native. Else, hardcode.
|
# Only do link tests if native. Else, hardcode.
|
||||||
if $GLIBCXX_IS_NATIVE; then
|
if $GLIBCXX_IS_NATIVE; then
|
||||||
|
|
||||||
|
@ -282,6 +284,9 @@ if $GLIBCXX_IS_NATIVE; then
|
||||||
# For Networking TS.
|
# For Networking TS.
|
||||||
AC_CHECK_FUNCS(sockatmark)
|
AC_CHECK_FUNCS(sockatmark)
|
||||||
|
|
||||||
|
# Non-standard functions used by C++17 std::from_chars
|
||||||
|
AC_CHECK_FUNCS(uselocale)
|
||||||
|
|
||||||
# For iconv support.
|
# For iconv support.
|
||||||
AM_ICONV
|
AM_ICONV
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,8 @@ case "${host}" in
|
||||||
# We don't yet support AIX's TLS ABI.
|
# We don't yet support AIX's TLS ABI.
|
||||||
#GCC_CHECK_TLS
|
#GCC_CHECK_TLS
|
||||||
AM_ICONV
|
AM_ICONV
|
||||||
|
|
||||||
|
AC_DEFINE(HAVE_USELOCALE)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*-darwin*)
|
*-darwin*)
|
||||||
|
@ -73,6 +75,8 @@ case "${host}" in
|
||||||
# Don't call GLIBCXX_CHECK_LINKER_FEATURES, Darwin doesn't have a GNU ld
|
# Don't call GLIBCXX_CHECK_LINKER_FEATURES, Darwin doesn't have a GNU ld
|
||||||
GLIBCXX_CHECK_MATH_SUPPORT
|
GLIBCXX_CHECK_MATH_SUPPORT
|
||||||
GLIBCXX_CHECK_STDLIB_SUPPORT
|
GLIBCXX_CHECK_STDLIB_SUPPORT
|
||||||
|
|
||||||
|
AC_CHECK_FUNCS(uselocale)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*djgpp)
|
*djgpp)
|
||||||
|
@ -129,6 +133,7 @@ case "${host}" in
|
||||||
AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
|
AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
|
||||||
AC_CHECK_FUNCS(timespec_get)
|
AC_CHECK_FUNCS(timespec_get)
|
||||||
AC_CHECK_FUNCS(sockatmark)
|
AC_CHECK_FUNCS(sockatmark)
|
||||||
|
AC_CHECK_FUNCS(uselocale)
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*-fuchsia*)
|
*-fuchsia*)
|
||||||
|
@ -190,6 +195,7 @@ case "${host}" in
|
||||||
AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
|
AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc)
|
||||||
AC_CHECK_FUNCS(timespec_get)
|
AC_CHECK_FUNCS(timespec_get)
|
||||||
AC_CHECK_FUNCS(sockatmark)
|
AC_CHECK_FUNCS(sockatmark)
|
||||||
|
AC_CHECK_FUNCS(uselocale)
|
||||||
AM_ICONV
|
AM_ICONV
|
||||||
;;
|
;;
|
||||||
*-mingw32*)
|
*-mingw32*)
|
||||||
|
|
|
@ -688,6 +688,20 @@ namespace __detail
|
||||||
operator^=(chars_format& __lhs, chars_format __rhs) noexcept
|
operator^=(chars_format& __lhs, chars_format __rhs) noexcept
|
||||||
{ return __lhs = __lhs ^ __rhs; }
|
{ 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
|
_GLIBCXX_END_NAMESPACE_VERSION
|
||||||
} // namespace std
|
} // namespace std
|
||||||
#endif // C++14
|
#endif // C++14
|
||||||
|
|
|
@ -50,6 +50,7 @@ inst_sources =
|
||||||
endif
|
endif
|
||||||
|
|
||||||
sources = \
|
sources = \
|
||||||
|
floating_from_chars.cc \
|
||||||
fs_dir.cc \
|
fs_dir.cc \
|
||||||
fs_ops.cc \
|
fs_ops.cc \
|
||||||
fs_path.cc \
|
fs_path.cc \
|
||||||
|
|
|
@ -124,8 +124,8 @@ LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||||
libc__17convenience_la_LIBADD =
|
libc__17convenience_la_LIBADD =
|
||||||
@ENABLE_DUAL_ABI_TRUE@am__objects_1 = cow-fs_dir.lo cow-fs_ops.lo \
|
@ENABLE_DUAL_ABI_TRUE@am__objects_1 = cow-fs_dir.lo cow-fs_ops.lo \
|
||||||
@ENABLE_DUAL_ABI_TRUE@ cow-fs_path.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_2 = floating_from_chars.lo fs_dir.lo fs_ops.lo fs_path.lo \
|
||||||
$(am__objects_1)
|
memory_resource.lo $(am__objects_1)
|
||||||
@ENABLE_DUAL_ABI_TRUE@am__objects_3 = cow-string-inst.lo
|
@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@am__objects_4 = ostream-inst.lo \
|
||||||
@ENABLE_EXTERN_TEMPLATE_TRUE@ string-inst.lo $(am__objects_3)
|
@ENABLE_EXTERN_TEMPLATE_TRUE@ string-inst.lo $(am__objects_3)
|
||||||
|
@ -435,6 +435,7 @@ headers =
|
||||||
@ENABLE_EXTERN_TEMPLATE_TRUE@ $(extra_string_inst_sources)
|
@ENABLE_EXTERN_TEMPLATE_TRUE@ $(extra_string_inst_sources)
|
||||||
|
|
||||||
sources = \
|
sources = \
|
||||||
|
floating_from_chars.cc \
|
||||||
fs_dir.cc \
|
fs_dir.cc \
|
||||||
fs_ops.cc \
|
fs_ops.cc \
|
||||||
fs_path.cc \
|
fs_path.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
|
|
@ -41,3 +41,4 @@ test01(const char* first, const char* last)
|
||||||
}
|
}
|
||||||
|
|
||||||
// { dg-prune-output "enable_if" }
|
// { dg-prune-output "enable_if" }
|
||||||
|
// { dg-prune-output "cannot bind non-const lvalue reference" }
|
||||||
|
|
|
@ -44,3 +44,4 @@ test01(const char* first, const char* last)
|
||||||
}
|
}
|
||||||
|
|
||||||
// { dg-prune-output "enable_if" }
|
// { dg-prune-output "enable_if" }
|
||||||
|
// { dg-prune-output "cannot bind non-const lvalue reference" }
|
||||||
|
|
|
@ -55,6 +55,12 @@ test01()
|
||||||
VERIFY( r.ptr == s.data() );
|
VERIFY( r.ptr == s.data() );
|
||||||
VERIFY( i == 999 );
|
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;
|
unsigned u = 888;
|
||||||
s = "-1";
|
s = "-1";
|
||||||
r = std::from_chars(s.data(), s.data() + s.length(), u);
|
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.ec == std::errc::invalid_argument );
|
||||||
VERIFY( r.ptr == s.data() );
|
VERIFY( r.ptr == s.data() );
|
||||||
VERIFY( u == 888 );
|
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)
|
for (int base = 2; base <= 36; ++base)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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.26");
|
||||||
known_versions.push_back("GLIBCXX_3.4.27");
|
known_versions.push_back("GLIBCXX_3.4.27");
|
||||||
known_versions.push_back("GLIBCXX_3.4.28");
|
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_1.3");
|
||||||
known_versions.push_back("CXXABI_LDBL_1.3");
|
known_versions.push_back("CXXABI_LDBL_1.3");
|
||||||
known_versions.push_back("CXXABI_1.3.1");
|
known_versions.push_back("CXXABI_1.3.1");
|
||||||
|
@ -240,7 +242,10 @@ check_version(symbol& test, bool added)
|
||||||
test.version_status = symbol::incompatible;
|
test.version_status = symbol::incompatible;
|
||||||
|
|
||||||
// Check that added symbols are added in the latest pre-release version.
|
// 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_1.3.12"
|
||||||
|| test.version_name == "CXXABI_FLOAT128"
|
|| test.version_name == "CXXABI_FLOAT128"
|
||||||
|| test.version_name == "CXXABI_TM_1");
|
|| test.version_name == "CXXABI_TM_1");
|
||||||
|
|
Loading…
Reference in New Issue