[libc-commits] [libc] [libcxx] [llvm] [libcxx][libc] Hand in Hand PoC with from_chars (PR #91651)
Mark de Wever via libc-commits
libc-commits at lists.llvm.org
Sun Oct 20 10:07:53 PDT 2024
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/91651
>From d79aabb1df3ca4fc9acc3beee3db2be29b3eb868 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Tue, 7 May 2024 16:50:24 -0700
Subject: [PATCH] [libcxx][libc] Hand in Hand PoC with from_chars
DO NOT MERGE, PROOF OF CONCEPT ONLY.
This patch aims to demonstrate the utility of sharing code between libc
and libc++ by using the libc float conversion code in the libc++
function from_chars. This patch adds from_chars for float and double
(long double is possible but was causing errors so was skipped here), as
well as a test to demonstrate that it works.
This is very much just a proof of concept, not intended to be committed
as-is. The from_chars code written is copied from the libc parsing code
and is not functionally complete, nor does it follow the correct coding
style.
---
libc/shared/fp_bits.h | 22 +
libc/shared/str_to_float.h | 27 +
libc/shared/str_to_integer.h | 24 +
libc/src/__support/FPUtil/FPBits.h | 12 +
libc/src/__support/high_precision_decimal.h | 11 +
libc/src/__support/str_to_float.h | 26 +
libc/src/__support/str_to_integer.h | 11 +
libc/src/__support/str_to_num_result.h | 11 +
libcxx/docs/Status/Cxx17Papers.csv | 2 +-
libcxx/docs/Status/Cxx2cIssues.csv | 1 +
libcxx/include/CMakeLists.txt | 1 +
.../__charconv/from_chars_floating_point.h | 73 +
libcxx/include/__configuration/availability.h | 13 +
libcxx/include/charconv | 7 +
libcxx/include/module.modulemap | 1 +
libcxx/lib/abi/CHANGELOG.TXT | 7 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...bcxxabi.v1.stable.exceptions.nonew.abilist | 2 +
...xxabi.v1.stable.noexceptions.nonew.abilist | 4 +-
libcxx/src/CMakeLists.txt | 9 +-
libcxx/src/charconv.cpp | 12 +
.../src/include/from_chars_floating_point.h | 457 +++++
.../floating_point.pass.cpp | 1560 +++++++++++++++++
.../utilities/charconv/charconv.msvc/test.cpp | 34 +-
.../charconv/charconv.msvc/test.pass.cpp | 1 +
libcxx/test/support/charconv_test_helpers.h | 2 +
libcxx/test/support/test_macros.h | 4 +
libcxx/utils/libcxx/test/features.py | 8 +
.../cmake/Modules/FindLibcCommonUtils.cmake | 14 +
.../llvm-project-overlay/libc/BUILD.bazel | 13 +
36 files changed, 2362 insertions(+), 21 deletions(-)
create mode 100644 libc/shared/fp_bits.h
create mode 100644 libc/shared/str_to_float.h
create mode 100644 libc/shared/str_to_integer.h
create mode 100644 libcxx/include/__charconv/from_chars_floating_point.h
create mode 100644 libcxx/src/include/from_chars_floating_point.h
create mode 100644 libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
create mode 100644 runtimes/cmake/Modules/FindLibcCommonUtils.cmake
diff --git a/libc/shared/fp_bits.h b/libc/shared/fp_bits.h
new file mode 100644
index 00000000000000..2898c508b77727
--- /dev/null
+++ b/libc/shared/fp_bits.h
@@ -0,0 +1,22 @@
+//===-- Floating point number utils -----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_FP_BITS_H
+#define LLVM_LIBC_SHARED_FP_BITS_H
+
+#include "src/__support/FPUtil/FPBits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using fputil::FPBits;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_FP_BITS_H
diff --git a/libc/shared/str_to_float.h b/libc/shared/str_to_float.h
new file mode 100644
index 00000000000000..b133a28e26efcd
--- /dev/null
+++ b/libc/shared/str_to_float.h
@@ -0,0 +1,27 @@
+//===-- String to float conversion utils ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_STR_TO_FLOAT_H
+#define LLVM_LIBC_SHARED_STR_TO_FLOAT_H
+
+#include "src/__support/str_to_float.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using internal::ExpandedFloat;
+using internal::FloatConvertReturn;
+using internal::RoundDirection;
+
+using internal::binary_exp_to_float;
+using internal::decimal_exp_to_float;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_STR_TO_FLOAT_H
diff --git a/libc/shared/str_to_integer.h b/libc/shared/str_to_integer.h
new file mode 100644
index 00000000000000..15bee698d5a6b2
--- /dev/null
+++ b/libc/shared/str_to_integer.h
@@ -0,0 +1,24 @@
+//===-- String to int conversion utils --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_STR_TO_INTEGER_H
+#define LLVM_LIBC_SHARED_STR_TO_INTEGER_H
+
+#include "src/__support/str_to_integer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using LIBC_NAMESPACE::StrToNumResult;
+
+using internal::strtointeger;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SHARED_STR_TO_INTEGER_H
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 5d1f633bb56ee4..6da89091a8ced9 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This file is shared with libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
+
#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H
@@ -795,6 +801,12 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
// A generic class to manipulate C++ floating point formats.
// It derives its functionality to FPRepImpl above.
template <typename T>
diff --git a/libc/src/__support/high_precision_decimal.h b/libc/src/__support/high_precision_decimal.h
index 3e397574d4cbb7..ac11649d1d1686 100644
--- a/libc/src/__support/high_precision_decimal.h
+++ b/libc/src/__support/high_precision_decimal.h
@@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This file is shared with libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
+
#ifndef LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H
#define LLVM_LIBC_SRC___SUPPORT_HIGH_PRECISION_DECIMAL_H
@@ -23,6 +29,11 @@ struct LShiftTableEntry {
char const *power_of_five;
};
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
// This is used in both this file and in the main str_to_float.h.
// TODO: Figure out where to put this.
enum class RoundDirection { Up, Down, Nearest };
diff --git a/libc/src/__support/str_to_float.h b/libc/src/__support/str_to_float.h
index a452b3a55fdeb4..91569af5cb7679 100644
--- a/libc/src/__support/str_to_float.h
+++ b/libc/src/__support/str_to_float.h
@@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This file is shared with libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
+
#ifndef LLVM_LIBC_SRC___SUPPORT_STR_TO_FLOAT_H
#define LLVM_LIBC_SRC___SUPPORT_STR_TO_FLOAT_H
@@ -32,11 +38,21 @@
namespace LIBC_NAMESPACE_DECL {
namespace internal {
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
template <class T> struct ExpandedFloat {
typename fputil::FPBits<T>::StorageType mantissa;
int32_t exponent;
};
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
template <class T> struct FloatConvertReturn {
ExpandedFloat<T> num = {0, 0};
int error = 0;
@@ -637,6 +653,11 @@ template <> LIBC_INLINE constexpr int32_t get_lower_bound<double>() {
return -(309 + 15 + 20);
}
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
// Takes a mantissa and base 10 exponent and converts it into its closest
// floating point type T equivalient. First we try the Eisel-Lemire algorithm,
// then if that fails then we fall back to a more accurate algorithm for
@@ -716,6 +737,11 @@ LIBC_INLINE FloatConvertReturn<T> decimal_exp_to_float(
return output;
}
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
// Takes a mantissa and base 2 exponent and converts it into its closest
// floating point type T equivalient. Since the exponent is already in the right
// form, this is mostly just shifting and rounding. This is used for hexadecimal
diff --git a/libc/src/__support/str_to_integer.h b/libc/src/__support/str_to_integer.h
index c8d02434c89ce2..86611f9a6902da 100644
--- a/libc/src/__support/str_to_integer.h
+++ b/libc/src/__support/str_to_integer.h
@@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This file is shared with libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
+
#ifndef LLVM_LIBC_SRC___SUPPORT_STR_TO_INTEGER_H
#define LLVM_LIBC_SRC___SUPPORT_STR_TO_INTEGER_H
@@ -73,6 +79,11 @@ LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
return 10;
}
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
// Takes a pointer to a string and the base to convert to. This function is used
// as the backend for all of the string to int functions.
template <class T>
diff --git a/libc/src/__support/str_to_num_result.h b/libc/src/__support/str_to_num_result.h
index 6d361357cac2a9..48c363c88ff419 100644
--- a/libc/src/__support/str_to_num_result.h
+++ b/libc/src/__support/str_to_num_result.h
@@ -6,6 +6,12 @@
//
//===----------------------------------------------------------------------===//
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This file is shared with libc++. You should also be careful when adding
+// dependencies to this file, since it needs to build for all libc++ targets.
+// -----------------------------------------------------------------------------
+
#ifndef LLVM_LIBC_SRC___SUPPORT_STR_TO_NUM_RESULT_H
#define LLVM_LIBC_SRC___SUPPORT_STR_TO_NUM_RESULT_H
@@ -16,6 +22,11 @@
namespace LIBC_NAMESPACE_DECL {
+// -----------------------------------------------------------------------------
+// **** WARNING ****
+// This interface is shared with libc++, if you change this interface you need
+// to update it in both libc and libc++.
+// -----------------------------------------------------------------------------
template <typename T> struct StrToNumResult {
T value;
int error;
diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv
index 3b56807312d556..7714f41ca19e04 100644
--- a/libcxx/docs/Status/Cxx17Papers.csv
+++ b/libcxx/docs/Status/Cxx17Papers.csv
@@ -71,7 +71,7 @@
"`P0394R4 <https://wg21.link/P0394R4>`__","Hotel Parallelifornia: terminate() for Parallel Algorithms Exception Handling","2016-06 (Oulu)","|Complete|","17.0",""
"","","","","",""
"`P0003R5 <https://wg21.link/P0003R5>`__","Removing Deprecated Exception Specifications from C++17","2016-11 (Issaquah)","|Complete|","5.0",""
-"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","``std::(to|from)_chars`` for integrals has been available since version 7.0. ``std::to_chars`` for ``float`` and ``double`` since version 14.0 ``std::to_chars`` for ``long double`` uses the implementation for ``double``."
+"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","``std::(to|from)_chars`` for integrals has been available since version 7.0. ``std::to_chars`` for ``float`` and ``double`` since version 14.0 ``std::to_chars`` for ``long double`` uses the implementation for ``double``. ``std::from_chars`` for ``float`` and ``double`` since version 20.0."
"`P0403R1 <https://wg21.link/P0403R1>`__","Literal suffixes for ``basic_string_view``\ ","2016-11 (Issaquah)","|Complete|","4.0",""
"`P0414R2 <https://wg21.link/P0414R2>`__","Merging shared_ptr changes from Library Fundamentals to C++17","2016-11 (Issaquah)","|Complete|","11.0",""
"`P0418R2 <https://wg21.link/P0418R2>`__","Fail or succeed: there is no atomic lattice","2016-11 (Issaquah)","","",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index a62c4992020a0f..19572c655ecd2e 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -78,4 +78,5 @@
"","","","","",""
"`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Adopted Yet","|Complete|","16.0",""
"`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in <=>","Not Adopted Yet","|Complete|","20.0",""
+"`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by std::from_chars is underspecified (option B)",,"Not Yet Adopted","|Complete|","20.0",""
"","","","","",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 3431ea7dab386b..3f8df714113680 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -235,6 +235,7 @@ set(files
__bit/rotate.h
__bit_reference
__charconv/chars_format.h
+ __charconv/from_chars_floating_point.h
__charconv/from_chars_integral.h
__charconv/from_chars_result.h
__charconv/tables.h
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
new file mode 100644
index 00000000000000..2860b0e8da83af
--- /dev/null
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -0,0 +1,73 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
+#define _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
+
+#include <__assert>
+#include <__charconv/chars_format.h>
+#include <__charconv/from_chars_result.h>
+#include <__config>
+#include <__system_error/errc.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 17
+
+template <class _Fp>
+struct __from_chars_result {
+ _Fp __value;
+ ptrdiff_t __n;
+ errc __ec;
+};
+
+template <class _Fp>
+_LIBCPP_EXPORTED_FROM_ABI __from_chars_result<_Fp> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt);
+
+extern template __from_chars_result<float> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt);
+
+extern template __from_chars_result<double> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt);
+
+template <class _Fp>
+_LIBCPP_HIDE_FROM_ABI from_chars_result
+__from_chars(const char* __first, const char* __last, _Fp& __value, chars_format __fmt) {
+ __from_chars_result<_Fp> __r = std::__from_chars_floating_point<_Fp>(__first, __last, __fmt);
+ if (__r.__ec != errc::invalid_argument)
+ __value = __r.__value;
+ return {__first + __r.__n, __r.__ec};
+}
+
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_HIDE_FROM_ABI inline from_chars_result
+from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general) {
+ return std::__from_chars<float>(__first, __last, __value, __fmt);
+}
+
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_HIDE_FROM_ABI inline from_chars_result
+from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general) {
+ return std::__from_chars<double>(__first, __last, __value, __fmt);
+}
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index f42ff460db4544..173999c46807c3 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -87,6 +87,9 @@
// in all versions of the library are available.
#if defined(_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)
+# define _LIBCPP_INTRODUCED_IN_LLVM_20 1
+# define _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE /* nothing */
+
# define _LIBCPP_INTRODUCED_IN_LLVM_19 1
# define _LIBCPP_INTRODUCED_IN_LLVM_19_ATTRIBUTE /* nothing */
@@ -132,6 +135,11 @@
// clang-format off
+// LLVM 20
+// TODO: Fill this in
+# define _LIBCPP_INTRODUCED_IN_LLVM_20 0
+# define _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE __attribute__((unavailable))
+
// LLVM 19
// TODO: Fill this in
# define _LIBCPP_INTRODUCED_IN_LLVM_19 0
@@ -409,6 +417,11 @@
#define _LIBCPP_AVAILABILITY_HAS_BAD_EXPECTED_ACCESS_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_19
#define _LIBCPP_AVAILABILITY_BAD_EXPECTED_ACCESS_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_19_ATTRIBUTE
+// This controls the availability of floating-point std::from_chars functions.
+// These overloads were added later than the integer overloads.
+#define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
+#define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
+
// Define availability attributes that depend on _LIBCPP_HAS_EXCEPTIONS.
// Those are defined in terms of the availability attributes above, and
// should not be vendor-specific.
diff --git a/libcxx/include/charconv b/libcxx/include/charconv
index a2e270e9316dc7..29c6875008abb4 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -65,6 +65,12 @@ namespace std {
constexpr from_chars_result from_chars(const char* first, const char* last,
see below& value, int base = 10); // constexpr since C++23
+ from_chars_result from_chars(const char* first, const char* last,
+ float& value, chars_format fmt);
+
+ from_chars_result from_chars(const char* first, const char* last,
+ double& value, chars_format fmt);
+
} // namespace std
*/
@@ -73,6 +79,7 @@ namespace std {
#if _LIBCPP_STD_VER >= 17
# include <__charconv/chars_format.h>
+# include <__charconv/from_chars_floating_point.h>
# include <__charconv/from_chars_integral.h>
# include <__charconv/from_chars_result.h>
# include <__charconv/tables.h>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 5a0e199394d018..03bb5bd7c6885d 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -898,6 +898,7 @@ module std [system] {
module charconv {
module chars_format { header "__charconv/chars_format.h" }
+ module from_chars_floating_point { header "__charconv/from_chars_floating_point.h" }
module from_chars_integral { header "__charconv/from_chars_integral.h" }
module from_chars_result { header "__charconv/from_chars_result.h" }
module tables { header "__charconv/tables.h" }
diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT
index 6911694b75d8a5..e27eb5fa046ff2 100644
--- a/libcxx/lib/abi/CHANGELOG.TXT
+++ b/libcxx/lib/abi/CHANGELOG.TXT
@@ -16,6 +16,13 @@ New entries should be added directly below the "Version" header.
Version 20.0
------------
+* [libcxx][libc] Implements from_chars floating-point
+
+ All platforms
+ -------------
+ Symbol added: _ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+ Symbol added: _ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE
+
* [libc++] Stop trying to avoid exporting some typeinfo names
This patch removes the explicit list of symbols to avoid exporting
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index db77e1d0ac30b6..79f999b3e02bb1 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1584,6 +1584,8 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 8af5db472f7c3a..9efdf11940a77f 100644
--- a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1220,6 +1220,8 @@
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERi', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 033d9f9987fa80..7fde4b905fc503 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1715,6 +1715,8 @@
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path19preferred_separatorE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 332d8abeb03e3a..da30346257f957 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1715,6 +1715,8 @@
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__122__libcpp_verbose_abortEPKcz', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem16_FilesystemClock9is_steadyE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem4path19preferred_separatorE', 'storage_mapping_class': 'RO', 'type': 'OBJECT'}
{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 62716f5c415f00..e1dc6e778b57c3 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1584,6 +1584,8 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6b77cda1e2866d..ceeeffe4d97925 100644
--- a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1220,6 +1220,8 @@
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 3458b333dd6a9b..d3670d237b239f 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1235,6 +1235,8 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index bdf90ba25c7fd9..2c21a03d41a0d7 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1233,6 +1233,8 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index ac3cc129c04b51..0d4c5095090878 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1204,6 +1204,8 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIdEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointIfEENS_19__from_chars_resultIT_EEPKcS5_NS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
@@ -2006,4 +2008,4 @@
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED0Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__114basic_iostreamIcNS_11char_traitsIcEEED1Ev', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD0Ev', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZTv0_n24_NSt3__19strstreamD1Ev', 'type': 'FUNC'}
\ No newline at end of file
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 4af04f202db1f7..cce8b8976f73c0 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -31,6 +31,7 @@ set(LIBCXX_SOURCES
include/ryu/f2s.h
include/ryu/ryu.h
include/to_chars_floating_point.h
+ include/from_chars_floating_point.h
legacy_pointer_safety.cpp
memory.cpp
memory_resource.cpp
@@ -172,11 +173,14 @@ endif()
split_list(LIBCXX_COMPILE_FLAGS)
split_list(LIBCXX_LINK_FLAGS)
+include(FindLibcCommonUtils)
+
# Build the shared library.
add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
- PRIVATE ${LIBCXX_LIBRARIES})
+ PRIVATE ${LIBCXX_LIBRARIES}
+ PRIVATE llvm-libc-common-utilities)
set_target_properties(cxx_shared
PROPERTIES
EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_SHARED}>,FALSE,TRUE>"
@@ -267,7 +271,8 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static
PRIVATE ${LIBCXX_LIBRARIES}
- PRIVATE libcxx-abi-static)
+ PRIVATE libcxx-abi-static
+ PRIVATE llvm-libc-common-utilities)
set_target_properties(cxx_static
PROPERTIES
EXCLUDE_FROM_ALL "$<IF:$<BOOL:${LIBCXX_ENABLE_STATIC}>,FALSE,TRUE>"
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 4fd7a2c2c0f038..3fe0afec0e283c 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -9,6 +9,7 @@
#include <charconv>
#include <string.h>
+#include "include/from_chars_floating_point.h"
#include "include/to_chars_floating_point.h"
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -74,4 +75,15 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
__first, __last, static_cast<double>(__value), __fmt, __precision);
}
+template <class _Fp>
+__from_chars_result<_Fp> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt) {
+ return std::__from_chars_floating_point_impl<_Fp>(__first, __last, __fmt);
+}
+
+template __from_chars_result<float> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt);
+
+template __from_chars_result<double> __from_chars_floating_point(
+ [[clang::noescape]] const char* __first, [[clang::noescape]] const char* __last, chars_format __fmt);
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
new file mode 100644
index 00000000000000..706e1d78633ddf
--- /dev/null
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -0,0 +1,457 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
+#define _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
+
+// These headers are in the shared LLVM-libc header library.
+#include "shared/fp_bits.h"
+#include "shared/str_to_float.h"
+#include "shared/str_to_integer.h"
+
+#include <__assert>
+#include <__config>
+#include <cctype>
+#include <charconv>
+#include <concepts>
+#include <limits>
+
+// Included for the _Floating_type_traits class
+#include "to_chars_floating_point.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+// Parses an infinity string.
+// Valid strings are case insensitive and contain INF or INFINITY.
+//
+// - __first is the first argument to std::from_chars. When the string is invalid
+// this value is returned as ptr in the result.
+// - __last is the last argument of std::from_chars.
+// - __value is the value argument of std::from_chars,
+// - __ptr is the current position is the input string. This is points beyond
+// the initial I character.
+// - __negative whether a valid string represents -inf or +inf.
+template <floating_point _Fp>
+__from_chars_result<_Fp>
+__from_chars_floating_point_inf(const char* const __first, const char* __last, const char* __ptr, bool __negative) {
+ if (__last - __ptr < 2) [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ if (std::tolower(__ptr[0]) != 'n' || std::tolower(__ptr[1]) != 'f') [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ __ptr += 2;
+
+ // At this point the result is valid and contains INF.
+ // When the remaining part contains INITY this will be consumed. Otherwise
+ // only INF is consumed. For example INFINITZ will consume INF and ignore
+ // INITZ.
+
+ if (__last - __ptr >= 5 //
+ && std::tolower(__ptr[0]) == 'i' //
+ && std::tolower(__ptr[1]) == 'n' //
+ && std::tolower(__ptr[2]) == 'i' //
+ && std::tolower(__ptr[3]) == 't' //
+ && std::tolower(__ptr[4]) == 'y')
+ __ptr += 5;
+
+ if constexpr (numeric_limits<_Fp>::has_infinity) {
+ if (__negative)
+ return {-std::numeric_limits<_Fp>::infinity(), __ptr - __first, std::errc{}};
+
+ return {std::numeric_limits<_Fp>::infinity(), __ptr - __first, std::errc{}};
+ } else {
+ return {_Fp{0}, __ptr - __first, errc::result_out_of_range};
+ }
+}
+
+// Parses a nan string.
+// Valid strings are case insensitive and contain INF or INFINITY.
+//
+// - __first is the first argument to std::from_chars. When the string is invalid
+// this value is returned as ptr in the result.
+// - __last is the last argument of std::from_chars.
+// - __value is the value argument of std::from_chars,
+// - __ptr is the current position is the input string. This is points beyond
+// the initial N character.
+// - __negative whether a valid string represents -nan or +nan.
+template <floating_point _Fp>
+__from_chars_result<_Fp>
+__from_chars_floating_point_nan(const char* const __first, const char* __last, const char* __ptr, bool __negative) {
+ if (__last - __ptr < 2) [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ if (std::tolower(__ptr[0]) != 'a' || std::tolower(__ptr[1]) != 'n') [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ __ptr += 2;
+
+ // At this point the result is valid and contains NAN. When the remaining
+ // part contains ( n-char-sequence_opt ) this will be consumed. Otherwise
+ // only NAN is consumed. For example NAN(abcd will consume NAN and ignore
+ // (abcd.
+ if (__last - __ptr >= 2 && __ptr[0] == '(') {
+ size_t __offset = 1;
+ do {
+ if (__ptr[__offset] == ')') {
+ __ptr += __offset + 1;
+ break;
+ }
+ if (__ptr[__offset] != '_' && !std::isalnum(__ptr[__offset]))
+ break;
+ ++__offset;
+ } while (__ptr + __offset != __last);
+ }
+
+ if (__negative)
+ return {-std::numeric_limits<_Fp>::quiet_NaN(), __ptr - __first, std::errc{}};
+
+ return {std::numeric_limits<_Fp>::quiet_NaN(), __ptr - __first, std::errc{}};
+}
+
+template <class _Tp>
+struct __fractional_constant_result {
+ size_t __offset{size_t(-1)};
+ _Tp __mantissa{0};
+ int __exponent{0};
+ bool __truncated{false};
+ bool __is_valid{false};
+};
+
+// Parses the hex constant part of the hexadecimal floating-point value.
+// - input start of buffer given to from_chars
+// - __n the number of elements in the buffer
+// - __offset where to start parsing. The input can have an optional sign, the
+// offset starts after this sign.
+template <class _Tp>
+__fractional_constant_result<_Tp> __parse_fractional_hex_constant(const char* __input, size_t __n, size_t __offset) {
+ __fractional_constant_result<_Tp> __result;
+
+ const _Tp __mantissa_truncate_threshold = numeric_limits<_Tp>::max() / 16;
+ bool __fraction = false;
+ for (; __offset < __n; ++__offset) {
+ if (std::isxdigit(__input[__offset])) {
+ __result.__is_valid = true;
+
+ uint32_t __digit = __input[__offset] - '0';
+ switch (std::tolower(__input[__offset])) {
+ case 'a':
+ __digit = 10;
+ break;
+ case 'b':
+ __digit = 11;
+ break;
+ case 'c':
+ __digit = 12;
+ break;
+ case 'd':
+ __digit = 13;
+ break;
+ case 'e':
+ __digit = 14;
+ break;
+ case 'f':
+ __digit = 15;
+ break;
+ }
+
+ if (__result.__mantissa < __mantissa_truncate_threshold) {
+ __result.__mantissa = (__result.__mantissa * 16) + __digit;
+ if (__fraction)
+ __result.__exponent -= 4;
+ } else {
+ if (__digit > 0)
+ __result.__truncated = true;
+ if (!__fraction)
+ __result.__exponent += 4;
+ }
+ } else if (__input[__offset] == '.') {
+ if (__fraction)
+ break; // this means that __input[__offset] points to a second decimal point, ending the number.
+
+ __fraction = true;
+ } else
+ break;
+ }
+
+ __result.__offset = __offset;
+ return __result;
+}
+
+struct __exponent_result {
+ size_t __offset{size_t(-1)};
+ int __value{0};
+ bool __present{false};
+};
+
+// When the exponent is not present the result of the struct contains
+// __offset, 0, false. This allows using the results unconditionally, the
+// __present is important for the scientific notation, where the value is
+// mandatory.
+__exponent_result __parse_exponent(const char* __input, size_t __n, size_t __offset, char __marker) {
+ if (__offset + 1 < __n && // an exponent always needs at least one digit.
+ std::tolower(__input[__offset]) == __marker && //
+ !std::isspace(__input[__offset + 1]) // leading whitespace is not allowed.
+ ) {
+ ++__offset;
+ LIBC_NAMESPACE::shared::StrToNumResult<int32_t> __e =
+ LIBC_NAMESPACE::shared::strtointeger<int32_t>(__input + __offset, 10, __n - __offset);
+ // __result.error contains the errno value, 0 or ERANGE these are not interesting.
+ // If the number of characters parsed is 0 it means there was no number.
+ if (__e.parsed_len != 0)
+ return {__offset + __e.parsed_len, __e.value, true};
+ else
+ --__offset; // the assumption of a valid exponent was not true, undo eating the exponent character.
+ }
+
+ return {__offset, 0, false};
+}
+
+// Here we do this operation as int64 to avoid overflow.
+int32_t __merge_exponents(int64_t __fractional, int64_t __exponent, int __max_biased_exponent) {
+ int64_t __sum = __fractional + __exponent;
+
+ if (__sum > __max_biased_exponent)
+ return __max_biased_exponent;
+
+ if (__sum < -__max_biased_exponent)
+ return -__max_biased_exponent;
+
+ return __sum;
+}
+
+template <class _Fp, class _Tp>
+__from_chars_result<_Fp>
+__calculate_result(_Tp __mantissa, int __exponent, bool __negative, __from_chars_result<_Fp> __result) {
+ auto __r = LIBC_NAMESPACE::shared::FPBits<_Fp>();
+ __r.set_mantissa(__mantissa);
+ __r.set_biased_exponent(__exponent);
+
+ // C17 7.12.1/6
+ // The result underflows if the magnitude of the mathematical result is so
+ // small that the mathematical result cannot be represented, without
+ // extraordinary roundoff error, in an object of the specified type.237) If
+ // the result underflows, the function returns an implementation-defined
+ // value whose magnitude is no greater than the smallest normalized positive
+ // number in the specified type; if the integer expression math_errhandling
+ // & MATH_ERRNO is nonzero, whether errno acquires the value ERANGE is
+ // implementation-defined; if the integer expression math_errhandling &
+ // MATH_ERREXCEPT is nonzero, whether the "underflow" floating-point
+ // exception is raised is implementation-defined.
+ //
+ // LLVM-LIBC sets ERAGNE for subnormal values
+ //
+ // [charconv.from.chars]/1
+ // ... If the parsed value is not in the range representable by the type of
+ // value, value is unmodified and the member ec of the return value is
+ // equal to errc::result_out_of_range. ...
+ //
+ // Undo the ERANGE for subnormal values.
+ if (__result.__ec == errc::result_out_of_range && __r.is_subnormal() && !__r.is_zero())
+ __result.__ec = errc{};
+
+ if (__negative)
+ __result.__value = -__r.get_val();
+ else
+ __result.__value = __r.get_val();
+
+ return __result;
+}
+
+// Implements from_chars for decimal floating-point values.
+// __first forwarded from from_chars
+// __last forwarded from from_chars
+// __value forwarded from from_chars
+// __fmt forwarded from from_chars
+// __ptr the start of the buffer to parse. This is after the optional sign character.
+// __negative should __value be set to a negative value?
+//
+// This function and __from_chars_floating_point_decimal are similar. However
+// the similar parts are all in helper functions. So the amount of code
+// duplication is minimal.
+template <floating_point _Fp>
+__from_chars_result<_Fp>
+__from_chars_floating_point_hex(const char* const __first, const char* __last, const char* __ptr, bool __negative) {
+ size_t __n = __last - __first;
+ ptrdiff_t __offset = __ptr - __first;
+
+ auto __fractional =
+ std::__parse_fractional_hex_constant<typename _Floating_type_traits<_Fp>::_Uint_type>(__first, __n, __offset);
+ if (!__fractional.__is_valid)
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ auto __parsed_exponent = std::__parse_exponent(__first, __n, __fractional.__offset, 'p');
+ __offset = __parsed_exponent.__offset;
+ int __exponent = std::__merge_exponents(
+ __fractional.__exponent, __parsed_exponent.__value, LIBC_NAMESPACE::shared::FPBits<_Fp>::MAX_BIASED_EXPONENT);
+
+ __from_chars_result<_Fp> __result{_Fp{0}, __offset, {}};
+ LIBC_NAMESPACE::shared::ExpandedFloat<_Fp> __expanded_float = {0, 0};
+ if (__fractional.__mantissa != 0) {
+ auto __temp = LIBC_NAMESPACE::shared::binary_exp_to_float<_Fp>(
+ {__fractional.__mantissa, __exponent},
+ __fractional.__truncated,
+ LIBC_NAMESPACE::shared::RoundDirection::Nearest);
+ __expanded_float = __temp.num;
+ if (__temp.error == ERANGE) {
+ __result.__ec = errc::result_out_of_range;
+ }
+ }
+
+ return std::__calculate_result<_Fp>(__expanded_float.mantissa, __expanded_float.exponent, __negative, __result);
+}
+
+// Parses the hex constant part of the decimal float value.
+// - input start of buffer given to from_chars
+// - __n the number of elements in the buffer
+// - __offset where to start parsing. The input can have an optional sign, the
+// offset starts after this sign.
+template <class _Tp>
+__fractional_constant_result<_Tp>
+__parse_fractional_decimal_constant(const char* __input, ptrdiff_t __n, ptrdiff_t __offset) {
+ __fractional_constant_result<_Tp> __result;
+
+ const _Tp __mantissa_truncate_threshold = numeric_limits<_Tp>::max() / 10;
+ bool __fraction = false;
+ for (; __offset < __n; ++__offset) {
+ if (std::isdigit(__input[__offset])) {
+ __result.__is_valid = true;
+
+ uint32_t __digit = __input[__offset] - '0';
+ if (__result.__mantissa < __mantissa_truncate_threshold) {
+ __result.__mantissa = (__result.__mantissa * 10) + __digit;
+ if (__fraction)
+ --__result.__exponent;
+ } else {
+ if (__digit > 0)
+ __result.__truncated = true;
+ if (!__fraction)
+ ++__result.__exponent;
+ }
+ } else if (__input[__offset] == '.') {
+ if (__fraction)
+ break; // this means that __input[__offset] points to a second decimal point, ending the number.
+
+ __fraction = true;
+ } else
+ break;
+ }
+
+ __result.__offset = __offset;
+ return __result;
+}
+
+// Implements from_chars for decimal floating-point values.
+// __first forwarded from from_chars
+// __last forwarded from from_chars
+// __value forwarded from from_chars
+// __fmt forwarded from from_chars
+// __ptr the start of the buffer to parse. This is after the optional sign character.
+// __negative should __value be set to a negative value?
+template <floating_point _Fp>
+__from_chars_result<_Fp> __from_chars_floating_point_decimal(
+ const char* const __first, const char* __last, chars_format __fmt, const char* __ptr, bool __negative) {
+ ptrdiff_t __n = __last - __first;
+ ptrdiff_t __offset = __ptr - __first;
+
+ auto __fractional =
+ std::__parse_fractional_decimal_constant<typename _Floating_type_traits<_Fp>::_Uint_type>(__first, __n, __offset);
+ if (!__fractional.__is_valid)
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ __offset = __fractional.__offset;
+
+ // LWG3456 Pattern used by std::from_chars is underspecified
+ // This changes fixed to ignore a possible exponent instead of making its
+ // existance an error.
+ int __exponent;
+ if (__fmt == chars_format::fixed) {
+ __exponent =
+ std::__merge_exponents(__fractional.__exponent, 0, LIBC_NAMESPACE::shared::FPBits<_Fp>::MAX_BIASED_EXPONENT);
+ } else {
+ auto __parsed_exponent = std::__parse_exponent(__first, __n, __offset, 'e');
+ if (__fmt == chars_format::scientific && !__parsed_exponent.__present) {
+ // [charconv.from.chars]/6.2 if fmt has chars_format::scientific set but not chars_format::fixed,
+ // the otherwise optional exponent part shall appear;
+ return {_Fp{0}, 0, errc::invalid_argument};
+ }
+
+ __offset = __parsed_exponent.__offset;
+ __exponent = std::__merge_exponents(
+ __fractional.__exponent, __parsed_exponent.__value, LIBC_NAMESPACE::shared::FPBits<_Fp>::MAX_BIASED_EXPONENT);
+ }
+
+ __from_chars_result<_Fp> __result{_Fp{0}, __offset, {}};
+ LIBC_NAMESPACE::shared::ExpandedFloat<_Fp> __expanded_float = {0, 0};
+ if (__fractional.__mantissa != 0) {
+ // This function expects to parse a positive value. This means it does not
+ // take a __first, __n as arguments, since __first points to '-' for
+ // negative values.
+ auto __temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Fp>(
+ {__fractional.__mantissa, __exponent},
+ __fractional.__truncated,
+ LIBC_NAMESPACE::shared::RoundDirection::Nearest,
+ __ptr,
+ __last - __ptr);
+ __expanded_float = __temp.num;
+ if (__temp.error == ERANGE) {
+ __result.__ec = errc::result_out_of_range;
+ }
+ }
+
+ return std::__calculate_result(__expanded_float.mantissa, __expanded_float.exponent, __negative, __result);
+}
+
+template <floating_point _Fp>
+__from_chars_result<_Fp>
+__from_chars_floating_point_impl(const char* const __first, const char* __last, chars_format __fmt) {
+ if (__first == __last) [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+
+ const char* __ptr = __first;
+ bool __negative = *__ptr == '-';
+ if (__negative) {
+ ++__ptr;
+ if (__ptr == __last) [[unlikely]]
+ return {_Fp{0}, 0, errc::invalid_argument};
+ }
+
+ // [charconv.from.chars]
+ // [Note 1: If the pattern allows for an optional sign, but the string has
+ // no digit characters following the sign, no characters match the pattern.
+ // — end note]
+ // This is true for integrals, floating point allows -.0
+
+ // [charconv.from.chars]/6.2
+ // if fmt has chars_format::scientific set but not chars_format::fixed, the
+ // otherwise optional exponent part shall appear;
+ // Since INF/NAN do not have an exponent this value is not valid.
+ //
+ // LWG3456 Pattern used by std::from_chars is underspecified
+ // Does not address this point, but proposed option B does solve this issue,
+ // Both MSVC STL and libstdc++ implement this this behaviour.
+ switch (std::tolower(*__ptr)) {
+ case 'i':
+ return std::__from_chars_floating_point_inf<_Fp>(__first, __last, __ptr + 1, __negative);
+ case 'n':
+ if constexpr (numeric_limits<_Fp>::has_quiet_NaN)
+ // NOTE: The pointer passed here will be parsed in the default C locale.
+ // This is standard behavior (see https://eel.is/c++draft/charconv.from.chars), but may be unexpected.
+ return std::__from_chars_floating_point_nan<_Fp>(__first, __last, __ptr + 1, __negative);
+ return {_Fp{0}, 0, errc::invalid_argument};
+ }
+
+ if (__fmt == chars_format::hex)
+ return std::__from_chars_floating_point_hex<_Fp>(__first, __last, __ptr, __negative);
+
+ return std::__from_chars_floating_point_decimal<_Fp>(__first, __last, __fmt, __ptr, __negative);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
new file mode 100644
index 00000000000000..6faf0499c4c9bb
--- /dev/null
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
@@ -0,0 +1,1560 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// XFAIL: availability-fp_from_chars-missing
+
+// 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)
+
+#include <array>
+#include <charconv>
+#include <cmath>
+#include <cstring>
+#include <limits>
+#include <stdexcept>
+#include <system_error>
+
+#include "charconv_test_helpers.h"
+#include "test_macros.h"
+
+template <class F>
+void test_infinity(std::chars_format fmt) {
+ const char* s = "-InFiNiTyXXX";
+ { // I
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 2, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // In
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 3, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // InF
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InF
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFi
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 5, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 6, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNi
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 7, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiT
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 8, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNiTy
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiTy
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNiTyXXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiTyXXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+}
+
+template <class F>
+void test_nan(std::chars_format fmt) {
+ {
+ const char* s = "-NaN(1_A)XXX";
+ { // N
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 2, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // Na
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 3, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // NaN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 0, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 5, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 6, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 7, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 8, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_A)
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A)
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_A)XXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A)XXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ }
+ {
+ const char* s = "NaN()";
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 5);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // validates a n-char-sequences with an invalid value
+ std::array s = {'N', 'a', 'N', '(', ' ', ')'};
+ s[4] = 'a';
+ {
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s.data(), s.data() + s.size(), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s.data() + s.size());
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ for (auto c : "!@#$%^&*(-=+[]{}|\\;:'\",./<>?~` \t\v\r\n") {
+ F value = 0.25;
+ s[4] = c;
+ std::from_chars_result result = std::from_chars(s.data(), s.data() + s.size(), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s.data() + 3);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ }
+}
+
+template <class F>
+void test_fmt_independent(std::chars_format fmt) {
+ test_infinity<F>(fmt);
+ test_nan<F>(fmt);
+
+ { // first == last
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(nullptr, nullptr, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == nullptr);
+ assert(value == F(0.25));
+ }
+ { // only a sign
+ F value = 0.25;
+ const char* s = "-";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ { // only decimal separator
+ F value = 0.25;
+ const char* s = ".";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ { // sign and decimal separator
+ F value = 0.25;
+ const char* s = "-.";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ { // + sign is not allowed
+ F value = 0.25;
+ const char* s = "+0.25";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+}
+
+template <class F>
+struct test_basics {
+ void operator()() {
+ for (auto fmt : {std::chars_format::scientific,
+ std::chars_format::fixed,
+ /*std::chars_format::hex,*/ std::chars_format::general})
+ test_fmt_independent<F>(fmt);
+ }
+};
+
+template <class F>
+struct test_fixed {
+ void operator()() {
+ std::from_chars_result r;
+ F x = 0.25;
+
+ // *** Failures
+
+ { // Starts with invalid character
+ std::array s = {' ', '1'};
+ for (auto c : "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+ s[0] = c;
+ r = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s.data());
+ assert(x == F(0.25));
+ }
+ }
+
+ // *** Success
+
+ { // number followed by non-numeric values
+ const char* s = "001x";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.0));
+ }
+ { // no leading digit
+ const char* s = ".5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 2);
+ assert(x == F(0.5));
+ }
+ { // negative sign and no leading digit
+ const char* s = "-.5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(-0.5));
+ }
+
+ { // double deciamal point
+ const char* s = "1.25.78";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ { // exponenent no sign
+ const char* s = "1.5e10";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // exponenent capitalized no sign
+ const char* s = "1.5E10";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // exponenent + sign
+ const char* s = "1.5e+10";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // exponenent - sign
+ const char* s = "1.5e-10";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // Exponent no number
+ const char* s = "1.5e";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // Exponent sign no number
+ {
+ const char* s = "1.5e+";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e-";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ }
+ { // Exponent with whitespace
+ {
+ const char* s = "1.5e +1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e+ 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e -1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e- 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ }
+ { // double exponent
+ const char* s = "1.25e0e12";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ { // Exponent double sign
+ {
+ const char* s = "1.25e++12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e+-12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e-+12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e--12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ }
+ { // exponent hex prefix
+ const char* s = "1.25e0x12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ { // This number is halfway between two float values.
+ const char* s = "20040229";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 8);
+ assert(x == F(20040229));
+ }
+ { // Shifting mantissa exponent and no exponent
+ const char* s = "123.456";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.23456e2));
+ }
+ { // Shifting mantissa exponent and an exponent
+ const char* s = "123.456e3";
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(123.456));
+ }
+ { // Mantissa overflow
+ {
+ const char* s = "0.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0.111111111111111111111111111111111111111111));
+ }
+ {
+ const char* s = "111111111111.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(111111111111.111111111111111111111111111111111111111111));
+ }
+ }
+ { // Negative value
+ const char* s = "-0.25";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::fixed);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(-0.25));
+ }
+ }
+};
+
+template <class F>
+struct test_scientific {
+ void operator()() {
+ std::from_chars_result r;
+ F x = 0.25;
+
+ // *** Failures
+
+ { // Starts with invalid character
+ std::array s = {' ', '1', 'e', '0'};
+ for (auto c : "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+ s[0] = c;
+ r = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::scientific);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s.data());
+ assert(x == F(0.25));
+ }
+ }
+ { // No exponent
+ const char* s = "1.23";
+ r = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ { // Exponent no number
+ const char* s = "1.23e";
+ r = std::from_chars(s, s + strlen(s), x, std::chars_format::scientific);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ { // Exponent sign no number
+ {
+ const char* s = "1.5e+";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.5e-";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ }
+ { // Exponent with whitespace
+ {
+ const char* s = "1.5e +1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.5e+ 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.5e -1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.5e- 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ }
+ { // exponent double sign
+ {
+ const char* s = "1.25e++12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.25e+-12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.25e-+12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ {
+ const char* s = "1.25e--12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s);
+ assert(x == F(0.25));
+ }
+ }
+
+ // *** Success
+
+ { // number followed by non-numeric values
+ const char* s = "001e0x";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 5);
+ assert(x == F(1.0));
+ }
+
+ { // double deciamal point
+ const char* s = "1.25e0.78";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+
+ { // exponenent no sign
+ const char* s = "1.5e10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent capitalized no sign
+ const char* s = "1.5E10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent + sign
+ const char* s = "1.5e+10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent - sign
+ const char* s = "1.5e-10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e-10));
+ }
+ { // exponent hex prefix -> e0
+ const char* s = "1.25e0x12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+ { // double exponent
+ const char* s = "1.25e0e12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+ { // This number is halfway between two float values.
+ const char* s = "20040229e0";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 10);
+ assert(x == F(20040229));
+ }
+ { // Shifting mantissa exponent and an exponent
+ const char* s = "123.456e3";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 9);
+ assert(x == F(1.23456e5));
+ }
+ { // Mantissa overflow
+ {
+ const char* s = "0.111111111111111111111111111111111111111111e0";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0.111111111111111111111111111111111111111111));
+ }
+ {
+ const char* s = "111111111111.111111111111111111111111111111111111111111e0";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(111111111111.111111111111111111111111111111111111111111));
+ }
+ }
+ { // Negative value
+ const char* s = "-0.25e0";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(-0.25));
+ }
+ { // value is too big -> +inf
+ const char* s = "1e9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == std::numeric_limits<F>::infinity());
+ }
+ { // negative value is too big -> -inf
+ const char* s = "-1e9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == -std::numeric_limits<F>::infinity());
+ }
+ { // value is too small -> 0
+ const char* s = "1e-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(0.0));
+ }
+ { // negative value is too small -> -0
+ const char* s = "-1e-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::scientific);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(-0.0));
+ }
+ }
+};
+
+template <class F>
+struct test_general {
+ void operator()() {
+ std::from_chars_result r;
+ F x = 0.25;
+
+ // *** Failures
+
+ { // Starts with invalid character
+ std::array s = {' ', '1'};
+ for (auto c : "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+ s[0] = c;
+ r = std::from_chars(s.data(), s.data() + s.size(), x);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s.data());
+ assert(x == F(0.25));
+ }
+ }
+
+ // *** Success
+
+ { // number followed by non-numeric values
+ const char* s = "001x";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.0));
+ }
+ { // no leading digit
+ const char* s = ".5e0";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0.5));
+ }
+ { // negative sign and no leading digit
+ const char* s = "-.5e0";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 5);
+ assert(x == F(-0.5));
+ }
+ { // no leading digit
+ const char* s = ".5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 2);
+ assert(x == F(0.5));
+ }
+ { // negative sign and no leading digit
+ const char* s = "-.5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(-0.5));
+ }
+ { // double deciamal point
+ const char* s = "1.25.78";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ { // exponenent no sign
+ const char* s = "1.5e10";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent capitalized no sign
+ const char* s = "1.5E10";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent + sign
+ const char* s = "1.5e+10";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent - sign
+ const char* s = "1.5e-10";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e-10));
+ }
+ { // Exponent no number
+ const char* s = "1.5e";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ { // Exponent sign no number
+ {
+ const char* s = "1.5e+";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e-";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ }
+ { // Exponent with whitespace
+ {
+ const char* s = "1.5e +1";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e+ 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e -1";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ {
+ const char* s = "1.5e- 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.5));
+ }
+ }
+ { // exponent double sign
+ {
+ const char* s = "1.25e++12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e+-12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e-+12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ {
+ const char* s = "1.25e--12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ }
+ { // exponent hex prefix -> e0
+ const char* s = "1.25e0x12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+ { // double exponent
+ const char* s = "1.25e0e12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+ { // This number is halfway between two float values.
+ const char* s = "20040229";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 8);
+ assert(x == F(20040229));
+ }
+ { // Shifting mantissa exponent and no exponent
+ const char* s = "123.456";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.23456e2));
+ }
+ { // Shifting mantissa exponent and an exponent
+ const char* s = "123.456e3";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 9);
+ assert(x == F(1.23456e5));
+ }
+ { // Mantissa overflow
+ {
+ const char* s = "0.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0.111111111111111111111111111111111111111111));
+ }
+ {
+ const char* s = "111111111111.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(111111111111.111111111111111111111111111111111111111111));
+ }
+ }
+ { // Negative value
+ const char* s = "-0.25";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(-0.25));
+ }
+ { // value is too big -> +inf
+ const char* s = "1e9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == std::numeric_limits<F>::infinity());
+ }
+ { // negative value is too big -> -inf
+ const char* s = "-1e9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == -std::numeric_limits<F>::infinity());
+ }
+ { // value is too small -> 0
+ const char* s = "1e-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(0.0));
+ }
+ { // negative value is too small -> -0
+ const char* s = "-1e-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(-0.0));
+ }
+ }
+};
+
+template <class F>
+struct test_hex {
+ void operator()() {
+ std::from_chars_result r;
+ F x = 0.25;
+
+ // *** Failures
+
+ { // Starts with invalid character
+ std::array s = {' ', '1', 'e', '0'};
+ for (auto c : "ghijklmnopqrstuvwxyz"
+ "GHIJKLMNOPQRSTUVWXYZ"
+ "`~!@#$%^&*()_=[]{}\\|;:'\",/<>? \t\v\r\n") {
+ s[0] = c;
+ r = std::from_chars(s.data(), s.data() + s.size(), x, std::chars_format::hex);
+
+ assert(r.ec == std::errc::invalid_argument);
+ assert(r.ptr == s.data());
+ assert(x == F(0.25));
+ }
+ }
+
+ // *** Success
+
+ { // number followed by non-numeric values
+ const char* s = "001x";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(1.0));
+ }
+ { // no leading digit
+ const char* s = ".5p0";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x0.5p0));
+ }
+ { // negative sign and no leading digit
+ const char* s = "-.5p0";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 5);
+ assert(x == F(-0x0.5p0));
+ }
+ { // no leading digit
+ const char* s = ".5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 2);
+ assert(x == F(0x0.5p0));
+ }
+ { // negative sign and no leading digit
+ const char* s = "-.5";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(-0x0.5p0));
+ }
+ { // double deciamal point
+ const char* s = "1.25.78";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x1.25p0));
+ }
+ { // exponenent no sign
+ const char* s = "1.5p10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(0x1.5p10));
+ }
+ { // exponenent capitalized no sign
+ const char* s = "1.5P10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(0x1.5p10));
+ }
+ { // exponenent + sign
+ const char* s = "1.5p+10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(0x1.5p10));
+ }
+ { // exponenent - sign
+ const char* s = "1.5p-10";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(0x1.5p-10));
+ }
+ { // Exponent no number
+ const char* s = "1.5p";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ { // Exponent sign no number
+ {
+ const char* s = "1.5p+";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ {
+ const char* s = "1.5p-";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ }
+ { // Exponent with whitespace
+ {
+ const char* s = "1.5p +1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ {
+ const char* s = "1.5p+ 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ {
+ const char* s = "1.5p -1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ {
+ const char* s = "1.5p- 1";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == F(0x1.5p0));
+ }
+ }
+ { // Exponent double sign
+ {
+ const char* s = "1.25p++12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x1.25p0));
+ }
+ {
+ const char* s = "1.25p+-12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x1.25p0));
+ }
+ {
+ const char* s = "1.25p-+12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x1.25p0));
+ }
+ {
+ const char* s = "1.25p--12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(0x1.25p0));
+ }
+ }
+ { // exponent hex prefix -> p0
+ const char* s = "1.25p0x12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(0x1.25p0));
+ }
+ { // double exponent
+ const char* s = "1.25p0p12";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(0x1.25p0));
+ }
+ { // This number is halfway between two float values.
+ const char* s = "131CA25";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(0x131CA25p0));
+ }
+ { // Shifting mantissa exponent and no exponent
+ const char* s = "123.456";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(0x123.456p0));
+ }
+ { // Shifting mantissa exponent and an exponent
+ const char* s = "123.456p3";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 9);
+ assert(x == F(0x123.456p3));
+ }
+ { // Mantissa overflow
+ {
+ const char* s = "0.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0x0.111111111111111111111111111111111111111111p0));
+ }
+ {
+ const char* s = "111111111111.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0x111111111111.111111111111111111111111111111111111111111p0));
+ }
+ }
+ { // Negative value
+ const char* s = "-0.25";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(-0x0.25p0));
+ }
+ { // value is too big -> +inf
+ const char* s = "1p9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == std::numeric_limits<F>::infinity());
+ }
+ { // negative value is too big -> -inf
+ const char* s = "-1p9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == -std::numeric_limits<F>::infinity());
+ }
+ { // value is too small -> 0
+ const char* s = "1p-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(0.0));
+ }
+ { // negative value is too small -> -0
+ const char* s = "-1p-9999999999999999999999999999999999999999";
+
+ r = std::from_chars(s, s + std::strlen(s), x, std::chars_format::hex);
+ assert(r.ec == std::errc::result_out_of_range);
+ assert(r.ptr == s + strlen(s));
+ assert(x == F(-0.0));
+ }
+ }
+};
+
+// The test
+// test/std/utilities/charconv/charconv.msvc/test.cpp
+// uses random values. This tests contains errors found by this test.
+void test_random_errors() {
+ {
+ const char* s = "4.219902180869891e-2788";
+ const char* last = s + std::strlen(s) - 1;
+
+ // last + 1 contains a digit. When that value is parsed the exponent is
+ // e-2788 which returns std::errc::result_out_of_range and the value 0.
+ // the proper exponent is e-278, which can be represented by a double.
+
+ double value = 0.25;
+ std::from_chars_result result = std::from_chars(s, last, value);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == last);
+ assert(value == 4.219902180869891e-278);
+ }
+ {
+ const char* s = "7.411412e-39U";
+ const char* last = s + std::strlen(s) - 1;
+
+ float value = 0.25;
+ std::from_chars_result result = std::from_chars(s, last, value);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == last);
+ assert(value == 7.411412e-39F);
+ }
+}
+
+int main(int, char**) {
+ run<test_basics>(all_floats);
+ run<test_scientific>(all_floats);
+ run<test_fixed>(all_floats);
+ run<test_general>(all_floats);
+
+ run<test_hex>(all_floats);
+
+ test_random_errors();
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
index 30ee9adcd74bf0..ace6d46b879b01 100644
--- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
@@ -45,6 +45,7 @@
#include "float_hex_precision_to_chars_test_cases.hpp"
#include "float_scientific_precision_to_chars_test_cases.hpp"
#include "float_to_chars_test_cases.hpp"
+#include "floating_point_test_cases.hpp"
using namespace std;
@@ -589,8 +590,8 @@ void test_floating_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint
// "-1.2345678901234567e-100" or "-1.23456789e-10"
constexpr std::size_t buffer_size = IsDouble ? 24 : 15;
char buffer[buffer_size];
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
FloatingType val;
#endif
@@ -614,8 +615,7 @@ void test_floating_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint
{
const auto to_result = to_chars(buffer, end(buffer), input, chars_format::scientific);
assert_message_bits(to_result.ec == errc{}, "to_result.ec", bits);
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
const char* const last = to_result.ptr;
const auto from_result = from_chars(buffer, last, val);
@@ -623,7 +623,7 @@ void test_floating_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint
assert_message_bits(from_result.ptr == last, "from_result.ptr", bits);
assert_message_bits(from_result.ec == errc{}, "from_result.ec", bits);
assert_message_bits(_Bit_cast<UIntType>(val) == bits, "round-trip", bits);
-#endif
+#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT
}
{
@@ -656,8 +656,8 @@ void test_floating_hex_prefix(const conditional_t<IsDouble, std::uint64_t, std::
// "-1.fffffffffffffp+1023" or "-1.fffffep+127"
constexpr std::size_t buffer_size = IsDouble ? 22 : 14;
char buffer[buffer_size];
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
FloatingType val;
#endif
@@ -667,8 +667,8 @@ void test_floating_hex_prefix(const conditional_t<IsDouble, std::uint64_t, std::
const auto to_result = to_chars(buffer, end(buffer), input, chars_format::hex);
assert_message_bits(to_result.ec == errc{}, "(hex) to_result.ec", bits);
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
const char* const last = to_result.ptr;
const auto from_result = from_chars(buffer, last, val, chars_format::hex);
@@ -676,7 +676,7 @@ void test_floating_hex_prefix(const conditional_t<IsDouble, std::uint64_t, std::
assert_message_bits(from_result.ptr == last, "(hex) from_result.ptr", bits);
assert_message_bits(from_result.ec == errc{}, "(hex) from_result.ec", bits);
assert_message_bits(_Bit_cast<UIntType>(val) == bits, "(hex) round-trip", bits);
-#endif
+#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT
}
}
@@ -786,8 +786,7 @@ void test_floating_prefixes(mt19937_64& mt64) {
}
}
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
template <typename T>
void test_floating_from_chars(const chars_format fmt) {
test_from_chars<T>("", fmt, 0, inv_arg); // no characters
@@ -855,11 +854,13 @@ void test_floating_from_chars(const chars_format fmt) {
// The UCRT considers indeterminate NaN to be negative quiet NaN with no payload bits set.
// It parses "nan(ind)" and "-nan(ind)" identically.
+# ifdef _MSC_VER
test_from_chars<T>("nan(InD)", fmt, 8, errc{}, -qnan);
test_from_chars<T>("-nan(InD)", fmt, 9, errc{}, -qnan);
test_from_chars<T>("nan(SnAn)", fmt, 9, errc{}, nullopt, TestFromCharsMode::SignalingNaN);
test_from_chars<T>("-nan(SnAn)", fmt, 10, errc{}, nullopt, TestFromCharsMode::SignalingNaN);
+# endif
switch (fmt) {
case chars_format::general:
@@ -941,7 +942,7 @@ void test_floating_from_chars(const chars_format fmt) {
break;
}
}
-#endif
+#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT
template <typename T>
void test_floating_to_chars(
@@ -953,13 +954,11 @@ void test_floating_to_chars(
void all_floating_tests(mt19937_64& mt64) {
test_floating_prefixes(mt64);
-// TODO Enable once std::from_chars has floating point support.
-#if 0
+#ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT
for (const auto& fmt : {chars_format::general, chars_format::scientific, chars_format::fixed, chars_format::hex}) {
test_floating_from_chars<float>(fmt);
test_floating_from_chars<double>(fmt);
}
-
// Test rounding.
// See float_from_chars_test_cases.hpp in this directory.
@@ -993,7 +992,8 @@ void all_floating_tests(mt19937_64& mt64) {
for (const auto& p : floating_point_test_cases_double) {
test_from_chars<double>(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast<double>(p.second));
}
-#endif
+#endif // TEST_HAS_FROM_CHARS_FLOATING_POINT
+
// See float_to_chars_test_cases.hpp in this directory.
for (const auto& t : float_to_chars_test_cases) {
if (t.fmt == chars_format{}) {
diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp
index 09ef70ea9924e8..0031a12f7d25ba 100644
--- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp
@@ -22,6 +22,7 @@
// <charconv>
#include <type_traits>
+#include "test_macros.h"
// Work-around for sprintf_s's usage in the Microsoft tests.
#ifndef _WIN32
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index f5fbedbeb0dcdd..fcae09478457b6 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -317,6 +317,8 @@ auto all_unsigned = type_list<
>();
auto integrals = concat(all_signed, all_unsigned);
+auto all_floats = type_list< float, double >(); //TODO: Add long double
+
template <template <typename> class Fn, typename... Ts>
TEST_CONSTEXPR_CXX23 void
run(type_list<Ts...>)
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 5dcd775e8641e1..d4d419fb3545cf 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -262,6 +262,10 @@
#define TEST_IGNORE_NODISCARD (void)
+#if !(defined(_LIBCPP_VERSION) && !_LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT)
+# define TEST_HAS_FROM_CHARS_FLOATING_POINT
+#endif
+
namespace test_macros_detail {
template <class T, class U>
struct is_same { enum { value = 0};} ;
diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py
index 735eb5ac949dc0..bc723ca9897a1d 100644
--- a/libcxx/utils/libcxx/test/features.py
+++ b/libcxx/utils/libcxx/test/features.py
@@ -795,4 +795,12 @@ def check_gdb(cfg):
cfg.available_features,
),
),
+ # Tests that require std::from_chars(floating-point) in the built library
+ Feature(
+ name="availability-fp_from_chars-missing",
+ when=lambda cfg: BooleanExpression.evaluate(
+ "!libcpp-has-no-availability-markup && (stdlib=apple-libc++ && !_target-has-llvm-20)",
+ cfg.available_features,
+ ),
+ ),
]
diff --git a/runtimes/cmake/Modules/FindLibcCommonUtils.cmake b/runtimes/cmake/Modules/FindLibcCommonUtils.cmake
new file mode 100644
index 00000000000000..763dc81d8bd733
--- /dev/null
+++ b/runtimes/cmake/Modules/FindLibcCommonUtils.cmake
@@ -0,0 +1,14 @@
+#===--------------------------------------------------------------------===//
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for details.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===--------------------------------------------------------------------===//
+
+add_library(llvm-libc-common-utilities INTERFACE)
+# TODO: Reorganize the libc shared section so that it can be included without
+# adding the root "libc" directory to the include path.
+target_include_directories(llvm-libc-common-utilities INTERFACE ${CMAKE_CURRENT_LIST_DIR}/../../../libc)
+target_compile_definitions(llvm-libc-common-utilities INTERFACE LIBC_NAMESPACE=__llvm_libc_common_utils)
+target_compile_features(llvm-libc-common-utilities INTERFACE cxx_std_17)
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 8f3bbe68648fef..55fa4bc4a4a926 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -1393,6 +1393,19 @@ libc_support_library(
],
)
+########################## externally shared targets ###########################
+
+libc_support_library(
+ name = "libc_external_common",
+ hdrs = glob(["shared/*.h"]),
+ deps = [
+ ":__support_common",
+ ":__support_fputil_fp_bits",
+ ":__support_str_to_float",
+ ":__support_str_to_integer",
+ ],
+)
+
############################### errno targets ################################
libc_function(
More information about the libc-commits
mailing list