[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
Tue Aug 6 11:17:15 PDT 2024


https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/91651

>From 08de46ef7ed4d47ce7af473b5ea554d109811a9d Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 May 2024 16:50:24 -0700
Subject: [PATCH 1/9] [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/str_to_float.h                    |  29 ++++
 libcxx/include/CMakeLists.txt                 |   1 +
 .../__charconv/from_chars_floating_point.h    |  68 +++++++++
 libcxx/include/charconv                       |   1 +
 libcxx/src/CMakeLists.txt                     |   5 +-
 libcxx/src/charconv.cpp                       |  17 +++
 .../src/include/from_chars_floating_point.h   | 135 ++++++++++++++++++
 .../charconv.from.chars/float.pass.cpp        |  70 +++++++++
 libcxx/test/support/charconv_test_helpers.h   |   2 +
 9 files changed, 326 insertions(+), 2 deletions(-)
 create mode 100644 libc/shared/str_to_float.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/float.pass.cpp

diff --git a/libc/shared/str_to_float.h b/libc/shared/str_to_float.h
new file mode 100644
index 0000000000000..da70db11f6b82
--- /dev/null
+++ b/libc/shared/str_to_float.h
@@ -0,0 +1,29 @@
+//===-- 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::shared {
+
+// WARNING: This is a proof of concept. In future the interface point for libcxx
+// won't be using libc internal classes.
+
+template <class T>
+inline internal::FloatConvertReturn<T> decimal_exp_to_float(
+    internal::ExpandedFloat<T> init_num, bool truncated,
+    internal::RoundDirection round, const char *__restrict num_start,
+    const size_t num_len = cpp::numeric_limits<size_t>::max()) {
+  return internal::decimal_exp_to_float(init_num, truncated, round, num_start,
+                                        num_len);
+}
+} // namespace LIBC_NAMESPACE::shared
+
+#endif // LLVM_LIBC_SHARED_STR_TO_FLOAT_H
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8..43f9e5071f3b0 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 0000000000000..dd305ca8ea1dc
--- /dev/null
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -0,0 +1,68 @@
+// -*- 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 <__charconv/traits.h>
+#include <__config>
+#include <__system_error/errc.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_floating_point.h>
+
+#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
+
+from_chars_result from_chars_floating_point(
+    const char* __first, const char* __last, float& value, chars_format fmt = chars_format::general);
+
+from_chars_result from_chars_floating_point(
+    const char* __first, const char* __last, double& value, chars_format fmt = chars_format::general);
+
+// template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, _Tp& __value, chars_format fmt = chars_format::general) {
+//   return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general) {
+//   return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general) {
+//   return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+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);
+
+#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/charconv b/libcxx/include/charconv
index a2e270e9316dc..a8d5a5802d890 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -73,6 +73,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/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index fe9d2666fa4ca..41be5dcb2cbc4 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
@@ -179,7 +180,7 @@ split_list(LIBCXX_LINK_FLAGS)
 # Build the shared library.
 if (LIBCXX_ENABLE_SHARED)
   add_library(cxx_shared SHARED ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
   target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
                                    PRIVATE ${LIBCXX_LIBRARIES})
   set_target_properties(cxx_shared
@@ -272,7 +273,7 @@ set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
 # Build the static library.
 if (LIBCXX_ENABLE_STATIC)
   add_library(cxx_static STATIC ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-  target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
   target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static
                                    PRIVATE ${LIBCXX_LIBRARIES}
                                    PRIVATE libcxx-abi-static)
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 4fd7a2c2c0f03..7d04de2dc4b35 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,20 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
       __first, __last, static_cast<double>(__value), __fmt, __precision);
 }
 
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, float& value, chars_format fmt) {
+  return from_chars_floating_point<float>(__first, __last, value, fmt);
+}
+
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, double& value, chars_format fmt) {
+  return from_chars_floating_point<double>(__first, __last, value, fmt);
+}
+
+from_chars_result from_chars(const char* __first, const char* __last, float& __value, chars_format fmt) {
+  return std::from_chars_floating_point(__first, __last, __value, fmt);
+}
+
+from_chars_result from_chars(const char* __first, const char* __last, double& __value, chars_format fmt) {
+  return std::from_chars_floating_point(__first, __last, __value, 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 0000000000000..2efb3bcb4d780
--- /dev/null
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// NEVER DO THIS FOR REAL, this is just for demonstration purposes.
+#define LIBC_NAMESPACE libc_namespace_in_libcxx
+
+// This header is in the shared LLVM-libc header library.
+#include "shared/str_to_float.h"
+
+#include <__assert>
+#include <__config>
+#include <charconv>
+#include <limits>
+#include <type_traits>
+
+// Included for the _Floating_type_traits class
+#include "to_chars_floating_point.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& value, chars_format fmt) {
+  using _Traits    = _Floating_type_traits<_Tp>;
+  using _Uint_type = typename _Traits::_Uint_type;
+  ptrdiff_t length = __last - __first;
+  _LIBCPP_ASSERT_INTERNAL(length > 0, "");
+
+  // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
+  // chars_format::general case. Also, no sign, inf, or nan handling.
+  _LIBCPP_ASSERT_INTERNAL(fmt == std::chars_format::general, "");
+
+  const char* src = __first; // rename to match the libc code copied for this section.
+
+  _Uint_type mantissa            = 0;
+  int exponent                   = 0;
+  bool truncated                 = false;
+  bool seen_digit                = false;
+  bool after_decimal             = false;
+  size_t index                   = 0;
+  const size_t BASE              = 10;
+  constexpr char EXPONENT_MARKER = 'e';
+  constexpr char DECIMAL_POINT   = '.';
+
+  // The loop fills the mantissa with as many digits as it can hold
+  const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE;
+  while (index < length) {
+    if (LIBC_NAMESPACE::internal::isdigit(src[index])) {
+      uint32_t digit = src[index] - '0';
+      seen_digit     = true;
+
+      if (mantissa < bitstype_max_div_by_base) {
+        mantissa = (mantissa * BASE) + digit;
+        if (after_decimal) {
+          --exponent;
+        }
+      } else {
+        if (digit > 0)
+          truncated = true;
+        if (!after_decimal)
+          ++exponent;
+      }
+
+      ++index;
+      continue;
+    }
+    if (src[index] == DECIMAL_POINT) {
+      if (after_decimal) {
+        break; // this means that src[index] points to a second decimal point, ending the number.
+      }
+      after_decimal = true;
+      ++index;
+      continue;
+    }
+    // The character is neither a digit nor a decimal point.
+    break;
+  }
+
+  if (!seen_digit)
+    return {src + index, {}};
+
+  if (index < length && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
+    bool has_sign = false;
+    if (index + 1 < length && (src[index + 1] == '+' || src[index + 1] == '-')) {
+      has_sign = true;
+    }
+    if (index + 1 + static_cast<size_t>(has_sign) < length &&
+        LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
+      ++index;
+      auto result = LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10);
+      // if (result.has_error())
+      //   output.error = result.error;
+      int32_t add_to_exponent = result.value;
+      index += result.parsed_len;
+
+      // Here we do this operation as int64 to avoid overflow.
+      int64_t temp_exponent = static_cast<int64_t>(exponent) + static_cast<int64_t>(add_to_exponent);
+
+      // If the result is in the valid range, then we use it. The valid range is
+      // also within the int32 range, so this prevents overflow issues.
+      if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
+        exponent = LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
+      } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
+        exponent = -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
+      } else {
+        exponent = static_cast<int32_t>(temp_exponent);
+      }
+    }
+  }
+
+  LIBC_NAMESPACE::internal::ExpandedFloat<_Tp> expanded_float = {0, 0};
+  if (mantissa != 0) {
+    auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Tp>(
+        {mantissa, exponent}, truncated, LIBC_NAMESPACE::internal::RoundDirection::Nearest, src, length);
+    expanded_float = temp.num;
+    // Note: there's also an error value in temp.error. I'm not doing that error handling right now though.
+  }
+
+  auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
+  result.set_mantissa(expanded_float.mantissa);
+  result.set_biased_exponent(expanded_float.exponent);
+  value = result.get_val();
+  return {src + index, {}};
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
new file mode 100644
index 0000000000000..3419fc478fec2
--- /dev/null
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <charconv>
+
+// constexpr from_chars_result from_chars(const char* first, const char* last,
+//                                        Float& value, chars_format fmt = chars_format::general)
+
+#include <charconv>
+#include "test_macros.h"
+#include "charconv_test_helpers.h"
+
+template <typename T>
+struct test_basics {
+  TEST_CONSTEXPR_CXX23 void operator()() {
+    std::from_chars_result r;
+    T x;
+
+    {
+      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 + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 3);
+      assert(x == T(1.0));
+    }
+
+    {
+      char s[] = "1.5e10";
+
+      r = std::from_chars(s, s + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 6);
+      assert(x == T(1.5e10));
+    }
+
+    {
+      char s[] = "20040229";
+
+      // This number is halfway between two float values.
+      r = std::from_chars(s, s + sizeof(s), x);
+      assert(r.ec == std::errc{});
+      assert(r.ptr == s + 8);
+      assert(x == T(20040229));
+    }
+  }
+};
+
+TEST_CONSTEXPR_CXX23 bool test() {
+  run<test_basics>(all_floats);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index f5fbedbeb0dcd..fcae09478457b 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...>)

>From b5f244027be4d7f19d26e3f6901ce30b79084310 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Thu, 9 May 2024 16:04:43 -0700
Subject: [PATCH 2/9] fix warnings and add to modulemap

---
 libcxx/include/module.modulemap               | 19 ++++++++++---------
 .../src/include/from_chars_floating_point.h   |  8 ++++----
 2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e..6c0ee59e2cc12 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1075,18 +1075,19 @@ module std_private_bit_invert_if      [system] { header "__bit/invert_if.h" }
 module std_private_bit_popcount       [system] { header "__bit/popcount.h" }
 module std_private_bit_rotate         [system] { header "__bit/rotate.h" }
 
-module std_private_charconv_chars_format            [system] { header "__charconv/chars_format.h" }
-module std_private_charconv_from_chars_integral     [system] { header "__charconv/from_chars_integral.h" }
-module std_private_charconv_from_chars_result       [system] { header "__charconv/from_chars_result.h" }
-module std_private_charconv_tables                  [system] { header "__charconv/tables.h" }
-module std_private_charconv_to_chars                [system] { header "__charconv/to_chars.h" }
-module std_private_charconv_to_chars_base_10        [system] { header "__charconv/to_chars_base_10.h" }
-module std_private_charconv_to_chars_floating_point [system] { header "__charconv/to_chars_floating_point.h" }
-module std_private_charconv_to_chars_integral       [system] {
+module std_private_charconv_chars_format              [system] { header "__charconv/chars_format.h" }
+module std_private_charconv_from_chars_integral       [system] { header "__charconv/from_chars_integral.h" }
+module std_private_charconv_from_chars_result         [system] { header "__charconv/from_chars_result.h" }
+module std_private_charconv_tables                    [system] { header "__charconv/tables.h" }
+module std_private_charconv_to_chars                  [system] { header "__charconv/to_chars.h" }
+module std_private_charconv_to_chars_base_10          [system] { header "__charconv/to_chars_base_10.h" }
+module std_private_charconv_to_chars_floating_point   [system] { header "__charconv/to_chars_floating_point.h" }
+module std_private_charconv_from_chars_floating_point [system] { header "__charconv/from_chars_floating_point.h" }
+module std_private_charconv_to_chars_integral         [system] {
   header "__charconv/to_chars_integral.h"
   export std_private_charconv_traits
 }
-module std_private_charconv_to_chars_result         [system] {
+module std_private_charconv_to_chars_result           [system] {
   header "__charconv/to_chars_result.h"
   export *
 }
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index 2efb3bcb4d780..2a9f4bc84eb8c 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -51,7 +51,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
 
   // The loop fills the mantissa with as many digits as it can hold
   const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE;
-  while (index < length) {
+  while (index < static_cast<size_t>(length)) {
     if (LIBC_NAMESPACE::internal::isdigit(src[index])) {
       uint32_t digit = src[index] - '0';
       seen_digit     = true;
@@ -86,12 +86,12 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
   if (!seen_digit)
     return {src + index, {}};
 
-  if (index < length && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
+  if (index < static_cast<size_t>(length) && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
     bool has_sign = false;
-    if (index + 1 < length && (src[index + 1] == '+' || src[index + 1] == '-')) {
+    if (index + 1 < static_cast<size_t>(length) && (src[index + 1] == '+' || src[index + 1] == '-')) {
       has_sign = true;
     }
-    if (index + 1 + static_cast<size_t>(has_sign) < length &&
+    if (index + 1 + static_cast<size_t>(has_sign) < static_cast<size_t>(length) &&
         LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
       ++index;
       auto result = LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10);

>From 97ca620fe80a1691f33bc22033a20505b13b5161 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Fri, 24 May 2024 15:28:04 -0700
Subject: [PATCH 3/9] fix lint warnings and linkage errors

---
 .../__charconv/from_chars_floating_point.h    |  4 ++--
 libcxx/src/charconv.cpp                       | 20 +++++++++++--------
 .../src/include/from_chars_floating_point.h   |  6 +++---
 .../charconv.from.chars/float.pass.cpp        |  4 ++--
 4 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index dd305ca8ea1dc..d575dbbf611fe 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -32,10 +32,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if _LIBCPP_STD_VER >= 17
 
 from_chars_result from_chars_floating_point(
-    const char* __first, const char* __last, float& value, chars_format fmt = chars_format::general);
+    const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general);
 
 from_chars_result from_chars_floating_point(
-    const char* __first, const char* __last, double& value, chars_format fmt = chars_format::general);
+    const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general);
 
 // template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
 // inline from_chars_result
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 7d04de2dc4b35..978ca4cde8c80 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -75,20 +75,24 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
       __first, __last, static_cast<double>(__value), __fmt, __precision);
 }
 
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, float& value, chars_format fmt) {
-  return from_chars_floating_point<float>(__first, __last, value, fmt);
+from_chars_result
+from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+  return from_chars_floating_point<float>(__first, __last, __value, __fmt);
 }
 
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, double& value, chars_format fmt) {
-  return from_chars_floating_point<double>(__first, __last, value, fmt);
+from_chars_result
+from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+  return from_chars_floating_point<double>(__first, __last, __value, __fmt);
 }
 
-from_chars_result from_chars(const char* __first, const char* __last, float& __value, chars_format fmt) {
-  return std::from_chars_floating_point(__first, __last, __value, fmt);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+  return from_chars_floating_point(__first, __last, __value, __fmt);
 }
 
-from_chars_result from_chars(const char* __first, const char* __last, double& __value, chars_format fmt) {
-  return std::from_chars_floating_point(__first, __last, __value, fmt);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+  return from_chars_floating_point(__first, __last, __value, __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
index 2a9f4bc84eb8c..fa6a2b3c5e534 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -27,7 +27,7 @@
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& value, chars_format fmt) {
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& __value, chars_format __fmt) {
   using _Traits    = _Floating_type_traits<_Tp>;
   using _Uint_type = typename _Traits::_Uint_type;
   ptrdiff_t length = __last - __first;
@@ -35,7 +35,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
 
   // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
   // chars_format::general case. Also, no sign, inf, or nan handling.
-  _LIBCPP_ASSERT_INTERNAL(fmt == std::chars_format::general, "");
+  _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
 
   const char* src = __first; // rename to match the libc code copied for this section.
 
@@ -126,7 +126,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
   auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
   result.set_mantissa(expanded_float.mantissa);
   result.set_biased_exponent(expanded_float.exponent);
-  value = result.get_val();
+  __value = result.get_val();
   return {src + index, {}};
 }
 
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index 3419fc478fec2..bf39f649084b0 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -19,7 +19,7 @@
 
 template <typename T>
 struct test_basics {
-  TEST_CONSTEXPR_CXX23 void operator()() {
+  void operator()() {
     std::from_chars_result r;
     T x;
 
@@ -57,7 +57,7 @@ struct test_basics {
   }
 };
 
-TEST_CONSTEXPR_CXX23 bool test() {
+bool test() {
   run<test_basics>(all_floats);
 
   return true;

>From ae9736e472d8ae0a01bc1177a3e8419fc5bdd561 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 30 Jul 2024 13:17:09 -0700
Subject: [PATCH 4/9] address comments and create runtimes target

---
 .../__charconv/from_chars_floating_point.h    | 36 +++++++------------
 libcxx/include/charconv                       |  6 ++++
 libcxx/src/CMakeLists.txt                     | 10 +++---
 libcxx/src/charconv.cpp                       | 14 ++------
 .../src/include/from_chars_floating_point.h   |  3 --
 .../charconv.from.chars/float.pass.cpp        | 12 ++-----
 runtimes/cmake/Modules/FindLibcUtils.cmake    |  8 +++++
 7 files changed, 37 insertions(+), 52 deletions(-)
 create mode 100644 runtimes/cmake/Modules/FindLibcUtils.cmake

diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index d575dbbf611fe..15fe88e64f2b5 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -31,33 +31,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
-from_chars_result from_chars_floating_point(
-    const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt);
 
-from_chars_result from_chars_floating_point(
-    const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt);
 
-// template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, _Tp& __value, chars_format fmt = chars_format::general) {
-//   return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
+_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_floating_point(__first, __last, __value, __fmt);
+}
 
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general) {
-//   return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
-
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general) {
-//   return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
-
-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);
+_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_floating_point(__first, __last, __value, __fmt);
+}
 
 #endif // _LIBCPP_STD_VER >= 17
 
diff --git a/libcxx/include/charconv b/libcxx/include/charconv
index a8d5a5802d890..dfef8194a2a67 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
 
+  constexpr from_chars_result from_chars(const char* first, const char* last,
+                               float& value, chars_format fmt);
+
+  constexpr from_chars_result from_chars(const char* first, const char* last,
+                               double& value, chars_format fmt);
+
 } // namespace std
 
 */
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 41be5dcb2cbc4..6183f42252d82 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -177,11 +177,13 @@ endif()
 split_list(LIBCXX_COMPILE_FLAGS)
 split_list(LIBCXX_LINK_FLAGS)
 
+include(FindLibcUtils)
+
 # Build the shared library.
 if (LIBCXX_ENABLE_SHARED)
   add_library(cxx_shared SHARED ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
-  target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
+  target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared llvm-libc-shared-utilities
                                    PRIVATE ${LIBCXX_LIBRARIES})
   set_target_properties(cxx_shared
     PROPERTIES
@@ -273,8 +275,8 @@ set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
 # Build the static library.
 if (LIBCXX_ENABLE_STATIC)
   add_library(cxx_static STATIC ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
-  target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
-  target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static
+  target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static llvm-libc-shared-utilities
                                    PRIVATE ${LIBCXX_LIBRARIES}
                                    PRIVATE libcxx-abi-static)
   set_target_properties(cxx_static
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 978ca4cde8c80..767b6cc9f28dd 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -76,23 +76,13 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
 }
 
 from_chars_result
-from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
   return from_chars_floating_point<float>(__first, __last, __value, __fmt);
 }
 
 from_chars_result
-from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
   return from_chars_floating_point<double>(__first, __last, __value, __fmt);
 }
 
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
-from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt) {
-  return from_chars_floating_point(__first, __last, __value, __fmt);
-}
-
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
-from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt) {
-  return from_chars_floating_point(__first, __last, __value, __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
index fa6a2b3c5e534..74a45d8a6fb2e 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -9,9 +9,6 @@
 #ifndef _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
 #define _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
 
-// NEVER DO THIS FOR REAL, this is just for demonstration purposes.
-#define LIBC_NAMESPACE libc_namespace_in_libcxx
-
 // This header is in the shared LLVM-libc header library.
 #include "shared/str_to_float.h"
 
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index bf39f649084b0..2f13210702bea 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -10,8 +10,8 @@
 
 // <charconv>
 
-// constexpr 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,
+//                              Float& value, chars_format fmt = chars_format::general)
 
 #include <charconv>
 #include "test_macros.h"
@@ -57,14 +57,8 @@ struct test_basics {
   }
 };
 
-bool test() {
-  run<test_basics>(all_floats);
-
-  return true;
-}
-
 int main(int, char**) {
-  test();
+  run<test_basics>(all_floats);
 
   return 0;
 }
diff --git a/runtimes/cmake/Modules/FindLibcUtils.cmake b/runtimes/cmake/Modules/FindLibcUtils.cmake
new file mode 100644
index 0000000000000..a1378401629fa
--- /dev/null
+++ b/runtimes/cmake/Modules/FindLibcUtils.cmake
@@ -0,0 +1,8 @@
+add_library(llvm-libc-shared-utilities INTERFACE)
+# TODO: Reorganize the libc shared section so that it can be included without
+# adding the root "libc" directory to the include path.
+# TODO: Find a better way to solve the problem that ${CMAKE_SOURCE_DIR} is
+# rooted in the runtimes directory, which is why we need the ".."
+target_include_directories(llvm-libc-shared-utilities INTERFACE ${CMAKE_SOURCE_DIR}/../libc) 
+target_compile_definitions(llvm-libc-shared-utilities INTERFACE LIBC_NAMESPACE=__llvm_libc_shared_utils)
+target_compile_features(llvm-libc-shared-utilities INTERFACE cxx_std_17)

>From 9109f32823345a190d4db0d15b92baeb8d7cf605 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Aug 2024 16:40:26 +0200
Subject: [PATCH 5/9] Updates (part of) the ABI lists.

Other can be updated when their CI run fails.
---
 ...64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...werpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 ...known-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
 7 files changed, 14 insertions(+)

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 917388f86811f..7e227263ae10a 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
@@ -1587,6 +1587,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/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 033d9f9987fa8..e0aca153ca0a2 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
@@ -572,6 +572,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', '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 332d8abeb03e3..d3c3012c443b0 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
@@ -572,6 +572,8 @@
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
 {'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', '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 defe235a283c2..a9dd2a5851f7f 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
@@ -1587,6 +1587,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 6b77cda1e2866..55c69232375b0 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_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointEPKcS1_RfNS_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 3458b333dd6a9..356440b6cfd15 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_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-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 bdf90ba25c7fd..0fc3be345fa8d 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_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'}

>From bebeb8e44d47a3a5b128a530795afb6cd7df794b Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Aug 2024 18:02:20 +0200
Subject: [PATCH 6/9] Several improvements

- using availability macros
- handle leading whitespace
- implement inf and nan
- implement negative values
---
 .../__charconv/from_chars_floating_point.h    |   4 +-
 libcxx/include/__configuration/availability.h |  13 +
 libcxx/src/charconv.cpp                       |   4 +-
 .../src/include/from_chars_floating_point.h   | 169 ++++++-
 .../charconv.from.chars/float.pass.cpp        | 447 +++++++++++++++++-
 5 files changed, 609 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index 15fe88e64f2b5..33ecf7012cfb6 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -31,10 +31,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI from_chars_result
 __from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt);
 
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI from_chars_result
 __from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt);
 
 _LIBCPP_HIDE_FROM_ABI inline from_chars_result
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ab483a07c9c13..edd9df87ebca9 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
@@ -375,6 +383,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_NO_EXCEPTIONS.
 // Those are defined in terms of the availability attributes above, and
 // should not be vendor-specific.
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 767b6cc9f28dd..8299df46a6292 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -77,12 +77,12 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
 
 from_chars_result
 __from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
-  return from_chars_floating_point<float>(__first, __last, __value, __fmt);
+  return std::__from_chars_floating_point<float>(__first, __last, __value, __fmt);
 }
 
 from_chars_result
 __from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
-  return from_chars_floating_point<double>(__first, __last, __value, __fmt);
+  return std::__from_chars_floating_point<double>(__first, __last, __value, __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
index 74a45d8a6fb2e..4156837405588 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -14,7 +14,9 @@
 
 #include <__assert>
 #include <__config>
+#include <cctype>
 #include <charconv>
+#include <concepts>
 #include <limits>
 #include <type_traits>
 
@@ -23,18 +25,112 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& __value, chars_format __fmt) {
+// Parses an infinity string.
+// Valid strings are case insentitive 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 _Tp>
+from_chars_result __from_chars_floating_point_inf(
+    const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+  if (__last - __ptr < 2) [[unlikely]]
+    return {__first, errc::invalid_argument};
+
+  if (std::tolower(__ptr[0]) != 'n' || std::tolower(__ptr[1]) != 'f') [[unlikely]]
+    return {__first, 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<_Tp>::has_infinity) {
+    if (__negative)
+      __value = -std::numeric_limits<_Tp>::infinity();
+    else
+      __value = std::numeric_limits<_Tp>::infinity();
+
+    return {__ptr, std::errc{}};
+  } else {
+    return {__ptr, errc::result_out_of_range};
+  }
+}
+
+// Parses an infinita string.
+// Valid strings are case insentitive 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 _Tp>
+from_chars_result __from_chars_floating_point_nan(
+    const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+  if (__last - __ptr < 2) [[unlikely]]
+    return {__first, errc::invalid_argument};
+
+  if (std::tolower(__ptr[0]) != 'a' || std::tolower(__ptr[1]) != 'n') [[unlikely]]
+    return {__first, 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)
+    __value = -std::numeric_limits<_Tp>::quiet_NaN();
+  else
+    __value = std::numeric_limits<_Tp>::quiet_NaN();
+
+  return {__ptr, std::errc{}};
+}
+
+template <floating_point _Tp>
+from_chars_result __from_chars_floating_point_decimal(
+    const char* const __first,
+    const char* __last,
+    _Tp& __value,
+    chars_format __fmt,
+    const char* __ptr,
+    bool __negative) {
   using _Traits    = _Floating_type_traits<_Tp>;
   using _Uint_type = typename _Traits::_Uint_type;
   ptrdiff_t length = __last - __first;
   _LIBCPP_ASSERT_INTERNAL(length > 0, "");
 
-  // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
-  // chars_format::general case. Also, no sign, inf, or nan handling.
-  _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
-
-  const char* src = __first; // rename to match the libc code copied for this section.
+  const char* src = __ptr; // rename to match the libc code copied for this section.
 
   _Uint_type mantissa            = 0;
   int exponent                   = 0;
@@ -123,10 +219,67 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
   auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
   result.set_mantissa(expanded_float.mantissa);
   result.set_biased_exponent(expanded_float.exponent);
-  __value = result.get_val();
+  if (__negative)
+    __value = -result.get_val();
+  else
+    __value = result.get_val();
   return {src + index, {}};
 }
 
+template <floating_point _Tp>
+from_chars_result
+__from_chars_floating_point(const char* const __first, const char* __last, _Tp& __value, chars_format __fmt) {
+  if (__first == __last) [[unlikely]]
+    return {__first, errc::invalid_argument};
+
+  const char* __ptr = __first;
+
+  // skip whitespace
+  while (std::isspace(*__ptr)) {
+    ++__ptr;
+    if (__ptr == __last) [[unlikely]]
+      return {__first, errc::invalid_argument}; // is this valid??
+  }
+
+  bool __negative = *__ptr == '-';
+  if (__negative) {
+    ++__ptr;
+    if (__ptr == __last) [[unlikely]]
+      return {__first, errc::invalid_argument};
+  }
+
+  if (!std::isdigit(*__ptr)) {
+    // TODO Evaluate the other implementations
+    // [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.
+    // See LWG3456
+    if (__fmt == chars_format::scientific)
+      return {__first, errc::invalid_argument};
+
+    switch (std::tolower(*__ptr)) {
+    case 'i':
+      return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative);
+    case 'n':
+      if constexpr (numeric_limits<_Tp>::has_quiet_NaN)
+        return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative);
+      [[fallthrough]];
+    default:
+      return {__first, errc::invalid_argument};
+    }
+  }
+
+#if 1
+  _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
+#else
+  if (__fmt == chars_format::hex)
+    return std::__from_chars_floating_point_hex(__first, __last, __value);
+#endif
+
+  return std::__from_chars_floating_point_decimal(__first, __last, __value, __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/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index 2f13210702bea..99699c037b461 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -8,57 +8,472 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
 // <charconv>
 
 // from_chars_result from_chars(const char* first, const char* last,
 //                              Float& value, chars_format fmt = chars_format::general)
 
+#include <array>
 #include <charconv>
-#include "test_macros.h"
+#include <cmath>
+#include <limits>
+
 #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) {
+  { // 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));
+  }
+  if (fmt != std::chars_format::scientific) {
+    test_infinity<F>(fmt);
+    test_nan<F>(fmt);
+  } else {
+    { // infinity
+      F value                       = 0.25;
+      const char* s                 = "inf";
+      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));
+    }
+    { // nan
+      F value                       = 0.25;
+      const char* s                 = "nan";
+      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));
+    }
+  }
+  { // start with decimal separator
+    F value                       = 0.25;
+    const char* s                 = ".";
+    std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
 
-template <typename T>
+    assert(result.ec == std::errc::invalid_argument);
+    assert(result.ptr == s);
+    assert(value == F(0.25));
+  }
+  { // Invalid sign
+    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_general {
   void operator()() {
     std::from_chars_result r;
-    T x;
+    F x;
 
-    {
-      char s[] = "001x";
+    { // number followed by non-numeric valies
+      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 + sizeof(s), x);
+      r = std::from_chars(s, s + std::strlen(s), x);
       assert(r.ec == std::errc{});
       assert(r.ptr == s + 3);
-      assert(x == T(1.0));
+      assert(x == F(1.0));
     }
 
-    {
-      char s[] = "1.5e10";
+    { // 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));
+    }
 
-      r = std::from_chars(s, s + sizeof(s), x);
+    { // 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 == T(1.5e10));
+      assert(x == F(1.5e10));
     }
+    { // exponenent + sign
+      const char* s = "1.5e+10";
 
-    {
-      char s[] = "20040229";
+      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";
 
-      // This number is halfway between two float values.
-      r = std::from_chars(s, s + sizeof(s), x);
+      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 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));
+    }
+    { // 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 == T(20040229));
+      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));
+      }
+    }
+    { // Leading whitespace
+      const char* s = " \t\v\r\n0.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));
+    }
+    { // 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));
     }
   }
 };
 
 int main(int, char**) {
   run<test_basics>(all_floats);
+  run<test_general>(all_floats);
 
   return 0;
 }

>From 1e2f1df27045b9999d941799b38e27154dc42649 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Mon, 5 Aug 2024 20:26:58 +0200
Subject: [PATCH 7/9] CI Fixes.

---
 libcxx/src/include/from_chars_floating_point.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index 4156837405588..d40a230b884a0 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -18,6 +18,7 @@
 #include <charconv>
 #include <concepts>
 #include <limits>
+#include <cstring>
 #include <type_traits>
 
 // Included for the _Floating_type_traits class

>From dbd07ae869b7caa5bc5f0aaf4c43203cbfdcbcad Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Aug 2024 18:02:20 +0200
Subject: [PATCH 8/9] Several improvements

- address review comment
- CI fixes
- implemented scientific
- implemented fixed
- adjust pattern to subject sequence instead of allowing whitespace
- Implemented out of range issues
- Partly enabled the MSVC tests for from_chars floating-point
- Fixes exponent out of bounds reading
---
 .../src/include/from_chars_floating_point.h   | 151 +++--
 .../charconv.from.chars/float.pass.cpp        | 575 +++++++++++++++++-
 .../float_from_chars_test_cases.hpp           |   1 -
 .../utilities/charconv/charconv.msvc/test.cpp |  50 +-
 .../charconv/charconv.msvc/test.pass.cpp      |   3 +
 .../test/support/msvc_stdlib_force_include.h  |   1 +
 libcxx/test/support/test_macros.h             |   4 +
 7 files changed, 706 insertions(+), 79 deletions(-)

diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index d40a230b884a0..b7d142394620c 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -18,7 +18,6 @@
 #include <charconv>
 #include <concepts>
 #include <limits>
-#include <cstring>
 #include <type_traits>
 
 // Included for the _Floating_type_traits class
@@ -36,9 +35,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 // - __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 _Tp>
+template <floating_point _Fp>
 from_chars_result __from_chars_floating_point_inf(
-    const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+    const char* const __first, const char* __last, _Fp& __value, const char* __ptr, bool __negative) {
   if (__last - __ptr < 2) [[unlikely]]
     return {__first, errc::invalid_argument};
 
@@ -60,11 +59,11 @@ from_chars_result __from_chars_floating_point_inf(
       && std::tolower(__ptr[4]) == 'y')
     __ptr += 5;
 
-  if constexpr (numeric_limits<_Tp>::has_infinity) {
+  if constexpr (numeric_limits<_Fp>::has_infinity) {
     if (__negative)
-      __value = -std::numeric_limits<_Tp>::infinity();
+      __value = -std::numeric_limits<_Fp>::infinity();
     else
-      __value = std::numeric_limits<_Tp>::infinity();
+      __value = std::numeric_limits<_Fp>::infinity();
 
     return {__ptr, std::errc{}};
   } else {
@@ -72,7 +71,7 @@ from_chars_result __from_chars_floating_point_inf(
   }
 }
 
-// Parses an infinita string.
+// Parses a nan string.
 // Valid strings are case insentitive and contain INF or INFINITY.
 //
 // - __first is the first argument to std::from_chars. When the string is invalid
@@ -82,9 +81,9 @@ from_chars_result __from_chars_floating_point_inf(
 // - __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 _Tp>
+template <floating_point _Fp>
 from_chars_result __from_chars_floating_point_nan(
-    const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+    const char* const __first, const char* __last, _Fp& __value, const char* __ptr, bool __negative) {
   if (__last - __ptr < 2) [[unlikely]]
     return {__first, errc::invalid_argument};
 
@@ -111,32 +110,33 @@ from_chars_result __from_chars_floating_point_nan(
   }
 
   if (__negative)
-    __value = -std::numeric_limits<_Tp>::quiet_NaN();
+    __value = -std::numeric_limits<_Fp>::quiet_NaN();
   else
-    __value = std::numeric_limits<_Tp>::quiet_NaN();
+    __value = std::numeric_limits<_Fp>::quiet_NaN();
 
   return {__ptr, std::errc{}};
 }
 
-template <floating_point _Tp>
+template <floating_point _Fp>
 from_chars_result __from_chars_floating_point_decimal(
     const char* const __first,
     const char* __last,
-    _Tp& __value,
+    _Fp& __value,
     chars_format __fmt,
     const char* __ptr,
     bool __negative) {
-  using _Traits    = _Floating_type_traits<_Tp>;
+  using _Traits    = _Floating_type_traits<_Fp>;
   using _Uint_type = typename _Traits::_Uint_type;
-  ptrdiff_t length = __last - __first;
-  _LIBCPP_ASSERT_INTERNAL(length > 0, "");
 
-  const char* src = __ptr; // rename to match the libc code copied for this section.
+  const char* src  = __ptr; // rename to match the libc code copied for this section.
+  ptrdiff_t length = __last - src;
+  _LIBCPP_ASSERT_INTERNAL(length > 0, "");
 
   _Uint_type mantissa            = 0;
   int exponent                   = 0;
   bool truncated                 = false;
   bool seen_digit                = false;
+  bool has_valid_exponent        = false;
   bool after_decimal             = false;
   size_t index                   = 0;
   const size_t BASE              = 10;
@@ -145,6 +145,7 @@ from_chars_result __from_chars_floating_point_decimal(
 
   // The loop fills the mantissa with as many digits as it can hold
   const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE;
+
   while (index < static_cast<size_t>(length)) {
     if (LIBC_NAMESPACE::internal::isdigit(src[index])) {
       uint32_t digit = src[index] - '0';
@@ -178,7 +179,7 @@ from_chars_result __from_chars_floating_point_decimal(
   }
 
   if (!seen_digit)
-    return {src + index, {}};
+    return {__first, errc::invalid_argument};
 
   if (index < static_cast<size_t>(length) && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
     bool has_sign = false;
@@ -187,8 +188,10 @@ from_chars_result __from_chars_floating_point_decimal(
     }
     if (index + 1 + static_cast<size_t>(has_sign) < static_cast<size_t>(length) &&
         LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
+      has_valid_exponent = true;
       ++index;
-      auto result = LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10);
+      auto result =
+          LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10, static_cast<size_t>(length) - index);
       // if (result.has_error())
       //   output.error = result.error;
       int32_t add_to_exponent = result.value;
@@ -199,57 +202,102 @@ from_chars_result __from_chars_floating_point_decimal(
 
       // If the result is in the valid range, then we use it. The valid range is
       // also within the int32 range, so this prevents overflow issues.
-      if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
-        exponent = LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
-      } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
-        exponent = -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
+      if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT) {
+        exponent = LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT;
+      } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT) {
+        exponent = -LIBC_NAMESPACE::fputil::FPBits<_Fp>::MAX_BIASED_EXPONENT;
       } else {
         exponent = static_cast<int32_t>(temp_exponent);
       }
     }
   }
 
-  LIBC_NAMESPACE::internal::ExpandedFloat<_Tp> expanded_float = {0, 0};
+  // [charconv.from.chars]
+  switch (__fmt) {
+  case chars_format::scientific:
+    // 6.2 if fmt has chars_format::scientific set but not chars_format::fixed,
+    // the otherwise optional exponent part shall appear;
+    if (!has_valid_exponent)
+      return {__first, errc::invalid_argument};
+    break;
+  case chars_format::fixed:
+    // 6.3 if fmt has chars_format::fixed set but not chars_format::scientific,
+    // the optional exponent part shall not appear;
+    if (has_valid_exponent)
+      return {__first, errc::invalid_argument};
+    break;
+  case chars_format::general:
+  case chars_format::hex: // impossible but it silences the compiler
+    break;
+  }
+
+  LIBC_NAMESPACE::internal::ExpandedFloat<_Fp> expanded_float = {0, 0};
+  errc status{};
   if (mantissa != 0) {
-    auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Tp>(
+    auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Fp>(
         {mantissa, exponent}, truncated, LIBC_NAMESPACE::internal::RoundDirection::Nearest, src, length);
     expanded_float = temp.num;
-    // Note: there's also an error value in temp.error. I'm not doing that error handling right now though.
+    if (temp.error == ERANGE) {
+      status = errc::result_out_of_range;
+    }
   }
 
-  auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
+  auto result = LIBC_NAMESPACE::fputil::FPBits<_Fp>();
   result.set_mantissa(expanded_float.mantissa);
   result.set_biased_exponent(expanded_float.exponent);
+
+  // C17 7.12.1/6
+  // The result underflows if the magnitude of the mathematical result is so
+  // small that the mathematical re- sult 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.
+  //
+  // LLLVM-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 (status == errc::result_out_of_range && result.is_subnormal() && !result.is_zero())
+    status = errc{};
+
   if (__negative)
     __value = -result.get_val();
   else
     __value = result.get_val();
-  return {src + index, {}};
+
+  return {src + index, status};
 }
 
-template <floating_point _Tp>
+template <floating_point _Fp>
 from_chars_result
-__from_chars_floating_point(const char* const __first, const char* __last, _Tp& __value, chars_format __fmt) {
+__from_chars_floating_point(const char* const __first, const char* __last, _Fp& __value, chars_format __fmt) {
   if (__first == __last) [[unlikely]]
     return {__first, errc::invalid_argument};
 
   const char* __ptr = __first;
-
-  // skip whitespace
-  while (std::isspace(*__ptr)) {
-    ++__ptr;
-    if (__ptr == __last) [[unlikely]]
-      return {__first, errc::invalid_argument}; // is this valid??
-  }
-
-  bool __negative = *__ptr == '-';
+  bool __negative   = *__ptr == '-';
   if (__negative) {
     ++__ptr;
     if (__ptr == __last) [[unlikely]]
       return {__first, errc::invalid_argument};
   }
 
-  if (!std::isdigit(*__ptr)) {
+  // [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
+  switch (std::tolower(*__ptr)) {
+  case 'i':
     // TODO Evaluate the other implementations
     // [charconv.from.chars]/6.2
     //   if fmt has chars_format::scientific set but not chars_format::fixed,
@@ -259,20 +307,23 @@ __from_chars_floating_point(const char* const __first, const char* __last, _Tp&
     if (__fmt == chars_format::scientific)
       return {__first, errc::invalid_argument};
 
-    switch (std::tolower(*__ptr)) {
-    case 'i':
-      return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative);
-    case 'n':
-      if constexpr (numeric_limits<_Tp>::has_quiet_NaN)
-        return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative);
-      [[fallthrough]];
-    default:
+    return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative);
+  case 'n':
+    // TODO Evaluate the other implementations
+    // [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.
+    // See LWG3456
+    if (__fmt == chars_format::scientific)
       return {__first, errc::invalid_argument};
-    }
+    if constexpr (numeric_limits<_Fp>::has_quiet_NaN)
+      return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative);
+    return {__first, errc::invalid_argument};
   }
 
 #if 1
-  _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
+  _LIBCPP_ASSERT_INTERNAL(__fmt != std::chars_format::hex, "");
 #else
   if (__fmt == chars_format::hex)
     return std::__from_chars_floating_point_hex(__first, __last, __value);
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index 99699c037b461..c7aac46519c31 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -18,7 +18,10 @@
 #include <array>
 #include <charconv>
 #include <cmath>
+#include <cstring>
 #include <limits>
+#include <stdexcept>
+#include <system_error>
 
 #include "charconv_test_helpers.h"
 #include "test_macros.h"
@@ -312,7 +315,7 @@ void test_fmt_independent(std::chars_format fmt) {
       assert(value == F(0.25));
     }
   }
-  { // start with decimal separator
+  { // 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);
@@ -321,7 +324,16 @@ void test_fmt_independent(std::chars_format fmt) {
     assert(result.ptr == s);
     assert(value == F(0.25));
   }
-  { // Invalid sign
+  { // 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);
@@ -335,19 +347,422 @@ void test_fmt_independent(std::chars_format fmt) {
 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})
+    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));
+      }
+    }
+
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
+    }
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
+    }
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
+    }
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
+    }
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      assert(x == F(0.25));
+    }
+    { // 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::invalid_argument);
+      assert(r.ptr == s);
+      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));
+    }
+    { // 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));
+    }
+    { // 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));
+    }
+    { // 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));
+    }
+    { // 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));
+    }
+    { // 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));
+    }
+
+    // *** 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));
+    }
+    { // 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;
+    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);
 
-    { // number followed by non-numeric valies
+        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
@@ -359,7 +774,54 @@ struct test_general {
       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, std::chars_format::scientific);
+      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, std::chars_format::scientific);
+      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";
 
@@ -369,7 +831,6 @@ struct test_general {
       assert(r.ptr == s + 4);
       assert(x == F(1.25));
     }
-
     { // exponenent no sign
       const char* s = "1.5e10";
 
@@ -378,6 +839,14 @@ struct test_general {
       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";
 
@@ -394,6 +863,22 @@ struct test_general {
       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));
+    }
     { // exponent double sign
       const char* s = "1.25e++12";
 
@@ -452,14 +937,6 @@ struct test_general {
         assert(x == F(111111111111.111111111111111111111111111111111111111111));
       }
     }
-    { // Leading whitespace
-      const char* s = " \t\v\r\n0.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));
-    }
     { // Negative value
       const char* s = "-0.25";
 
@@ -468,12 +945,80 @@ struct test_general {
       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));
+    }
   }
 };
 
+// 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);
 
+  test_random_errors();
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp b/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp
index c0fbfcb132f7e..9fd5b0768442e 100644
--- a/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp
+++ b/libcxx/test/std/utilities/charconv/charconv.msvc/float_from_chars_test_cases.hpp
@@ -121,7 +121,6 @@ inline constexpr FloatFromCharsTestCase float_from_chars_test_cases[] = {
      "2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"
      "2222222222222222222e-45",
         chars_format::scientific, 1006, errc{}, 0x0.000004p-126f},
-
     // VSO-733765 "<charconv>: [Feedback] double std::from_chars behavior on exponent out of range"
     // LWG-3081 "Floating point from_chars API does not distinguish between overflow and underflow"
     // These test cases exercise every overflow/underflow codepath.
diff --git a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
index 30ee9adcd74bf..a5218c6b6d797 100644
--- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.cpp
@@ -27,6 +27,8 @@
 #include <utility>
 #include <vector>
 
+#include <ranges> // TODO REMOVE
+
 #include "double_fixed_precision_to_chars_test_cases_1.hpp"
 #include "double_fixed_precision_to_chars_test_cases_2.hpp"
 #include "double_fixed_precision_to_chars_test_cases_3.hpp"
@@ -45,6 +47,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;
 
@@ -389,7 +392,22 @@ void test_from_chars(const string_view input, const BaseOrFmt base_or_fmt, const
     if constexpr (is_integral_v<T>) {
         assert(mode == TestFromCharsMode::Normal);
     }
-
+#if 1
+    else {
+      // Not implemented yet.
+      if (base_or_fmt == std::chars_format::hex)
+        return;
+      // LWG3456. Pattern used by std::from_chars is underspecified
+      if (base_or_fmt == std::chars_format::scientific && correct_ec == std::errc{} && opt_correct &&
+          !std::isfinite(*opt_correct))
+        // Based on the wording in the Standard scientific can't be NaN or Inf.
+        return;
+      if (base_or_fmt == std::chars_format::fixed && correct_ec == std::errc{} &&
+          std::ranges::contains(input, 'e', [](char c) { return std::tolower(c); }))
+        // Fixed should not contain an exponent. MSVC terminates at e, libc++ marks as invalid.
+        return;
+    }
+#endif
     constexpr T unmodified = 111;
 
     T dest = unmodified;
@@ -589,8 +607,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 +632,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 +640,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
         }
 
         {
@@ -786,8 +803,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 +871,20 @@ 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.
+	//
+	//
+	// TODO Evaluate this code, probably needs to be commented out.
+	//
+	//
+#if 0
     test_from_chars<T>("nan(InD)", fmt, 8, errc{}, -qnan);
+#endif
     test_from_chars<T>("-nan(InD)", fmt, 9, errc{}, -qnan);
 
+#if 0
     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 +966,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 +978,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 +1016,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 09ef70ea9924e..190805687beb8 100644
--- a/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.msvc/test.pass.cpp
@@ -8,6 +8,8 @@
 
 // UNSUPPORTED: c++03, c++11, c++14
 
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
 // to_chars requires functions in the dylib that have not been introduced in older
 // versions of the dylib on macOS.
 // XFAIL: availability-fp_to_chars-missing
@@ -22,6 +24,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/msvc_stdlib_force_include.h b/libcxx/test/support/msvc_stdlib_force_include.h
index 785670224c3b1..6bfe6e39b711e 100644
--- a/libcxx/test/support/msvc_stdlib_force_include.h
+++ b/libcxx/test/support/msvc_stdlib_force_include.h
@@ -103,5 +103,6 @@ const AssertionDialogAvoider assertion_dialog_avoider{};
 #define TEST_ABI_MICROSOFT
 
 #define _LIBCPP_AVAILABILITY_THROW_BAD_ANY_CAST
+#define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT 1
 
 #endif // SUPPORT_MSVC_STDLIB_FORCE_INCLUDE_H
diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h
index 15fc5b69b5207..07845458871ba 100644
--- a/libcxx/test/support/test_macros.h
+++ b/libcxx/test/support/test_macros.h
@@ -260,6 +260,10 @@
 
 #define TEST_IGNORE_NODISCARD (void)
 
+#if defined(_LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT) && _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};} ;

>From 83ed1616bdc327a36fdf8e47c92b7b9bc4103dbc Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Tue, 6 Aug 2024 20:16:57 +0200
Subject: [PATCH 9/9] CI fixes

---
 .../utilities/charconv/charconv.from.chars/float.pass.cpp   | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index c7aac46519c31..4f55cdec77ba9 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -372,7 +372,7 @@ struct test_fixed {
 
         assert(r.ec == std::errc::invalid_argument);
         assert(r.ptr == s.data());
-        assert(x = F(0.25));
+        assert(x == F(0.25));
       }
     }
 
@@ -560,7 +560,7 @@ struct test_scientific {
 
         assert(r.ec == std::errc::invalid_argument);
         assert(r.ptr == s.data());
-        assert(x = F(0.25));
+        assert(x == F(0.25));
       }
     }
     { // No exponent
@@ -756,7 +756,7 @@ struct test_general {
 
         assert(r.ec == std::errc::invalid_argument);
         assert(r.ptr == s.data());
-        assert(x = F(0.25));
+        assert(x == F(0.25));
       }
     }
 



More information about the libc-commits mailing list