Skip to content

[libc++] Complete <charconv> for 64-bit long double platforms #117125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_string_view`` ``201606L``
---------------------------------------------------------- -----------------
``__cpp_lib_to_chars`` *unimplemented*
``__cpp_lib_to_chars`` ``201611L``
---------------------------------------------------------- -----------------
``__cpp_lib_transparent_operators`` ``201510L``
---------------------------------------------------------- -----------------
Expand Down Expand Up @@ -490,7 +490,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_text_encoding`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_to_chars`` *unimplemented*
``__cpp_lib_to_chars`` ``202306L``
---------------------------------------------------------- -----------------
``__cpp_lib_to_string`` *unimplemented*
---------------------------------------------------------- -----------------
Expand Down
4 changes: 4 additions & 0 deletions libcxx/docs/ReleaseNotes/20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ What's New in Libc++ 20.0.0?
Implemented Papers
------------------

- P0067R5: Elementary string conversions is implemented for platforms
where ``long double`` has the same format as ``double`` (`Github <https://github.com/llvm/llvm-project/issues/99940>`__)
- P0619R4: Reviewing Deprecated Facilities of C++17 for C++20 (`Github <https://github.com/llvm/llvm-project/issues/99985>`__)
- P0682R1: Repairing elementary string conversions is implemented for platforms
where ``long double`` has the same format as ``double`` (`Github <https://github.com/llvm/llvm-project/issues/99952>`__)
- P2747R2: ``constexpr`` placement new (`Github <https://github.com/llvm/llvm-project/issues/105427>`__)
- P2609R3: Relaxing Ranges Just A Smidge (`Github <https://github.com/llvm/llvm-project/issues/105253>`__)
- P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
Expand Down
4 changes: 2 additions & 2 deletions libcxx/docs/Status/Cxx17Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"`P0394R4 <https://wg21.link/P0394R4>`__","Hotel Parallelifornia: terminate() for Parallel Algorithms Exception Handling","2016-06 (Oulu)","|Complete|","17",""
"","","","","",""
"`P0003R5 <https://wg21.link/P0003R5>`__","Removing Deprecated Exception Specifications from C++17","2016-11 (Issaquah)","|Complete|","5",""
"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","For integer types, ``std::(to|from)_chars`` has been available since v7; for ``float`` and ``double``, ``std::to_chars`` since v14 and ``std::from_chars`` since v20. Support is complete except for ``long double``."
"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","For integer types, ``std::(to|from)_chars`` has been available since v7; for ``float`` and ``double``, ``std::to_chars`` since v14 and ``std::from_chars`` since v20. Support is complete except for ``long double`` on platforms where its format is different ``double``."
"`P0403R1 <https://wg21.link/P0403R1>`__","Literal suffixes for ``basic_string_view``\ ","2016-11 (Issaquah)","|Complete|","4",""
"`P0414R2 <https://wg21.link/P0414R2>`__","Merging shared_ptr changes from Library Fundamentals to C++17","2016-11 (Issaquah)","|Complete|","11",""
"`P0418R2 <https://wg21.link/P0418R2>`__","Fail or succeed: there is no atomic lattice","2016-11 (Issaquah)","","",""
Expand Down Expand Up @@ -109,5 +109,5 @@
"`P0618R0 <https://wg21.link/P0618R0>`__","Deprecating <codecvt>","2017-02 (Kona)","|Complete|","15",""
"`P0623R0 <https://wg21.link/P0623R0>`__","Final C++17 Parallel Algorithms Fixes","2017-02 (Kona)","|Nothing To Do|","",""
"","","","","",""
"`P0682R1 <https://wg21.link/P0682R1>`__","Repairing elementary string conversions","2017-07 (Toronto)","","",""
"`P0682R1 <https://wg21.link/P0682R1>`__","Repairing elementary string conversions","2017-07 (Toronto)","|Partial|","","Support is complete in v20 on platforms where ``long double`` has the same format as ``double``."
"`P0739R0 <https://wg21.link/P0739R0>`__","Some improvements to class template argument deduction integration into the standard library","2017-07 (Toronto)","|Complete|","5",""
12 changes: 12 additions & 0 deletions libcxx/include/__charconv/from_chars_floating_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ from_chars(const char* __first, const char* __last, double& __value, chars_forma
return std::__from_chars<double>(__first, __last, __value, __fmt);
}

# if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_HIDE_FROM_ABI inline from_chars_result
from_chars(const char* __first, const char* __last, long double& __value, chars_format __fmt = chars_format::general) {
double __dval;
const auto __result = std::__from_chars<double>(__first, __last, __dval, __fmt);
if (__result.ec != errc::invalid_argument)
__value = __dval;
return __result;
}
// TODO: Complete the implementation for platforms where long double has a different format from double.
# endif

#endif // _LIBCPP_STD_VER >= 17

_LIBCPP_END_NAMESPACE_STD
Expand Down
6 changes: 6 additions & 0 deletions libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,12 @@ typedef __char32_t char32_t;
# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER 0
# endif

# if defined(_MSC_VER) || __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed that if they have the same size they have the same representation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already using such condition in the test suite. I don't think we support any platform where their sizes are equal but their representations are not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm much less concerned about such a check in our test suite than in our ABI.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree it would be safer if the compiler provided some #define that told us basically "double and long double have the same size and representation". I don't know that this is provided by any compiler, though. One thing we could do is explicitly list the platforms on which we know that this is the case for certain. That way we'd be more conservative (i.e. the assumption may hold for some platforms that we wouldn't list, but at least we'd never make the assumptions for platforms where that's not true).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way to properly check that long double is the same as double is to use LDBL_MANT_DIG == 53.

# define _LIBCPP_LONG_DOUBLE_IS_DOUBLE 1
# else
# define _LIBCPP_LONG_DOUBLE_IS_DOUBLE 0
# endif

#endif // __cplusplus

#endif // _LIBCPP___CONFIG
8 changes: 6 additions & 2 deletions libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,9 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_shared_ptr_arrays 201611L
# define __cpp_lib_shared_ptr_weak_type 201606L
# define __cpp_lib_string_view 201606L
// # define __cpp_lib_to_chars 201611L
# if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
# define __cpp_lib_to_chars 201611L
# endif
# undef __cpp_lib_transparent_operators
# define __cpp_lib_transparent_operators 201510L
# define __cpp_lib_type_trait_variable_templates 201510L
Expand Down Expand Up @@ -572,8 +574,10 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_string_view 202403L
// # define __cpp_lib_submdspan 202306L
// # define __cpp_lib_text_encoding 202306L
# if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
# undef __cpp_lib_to_chars
// # define __cpp_lib_to_chars 202306L
# define __cpp_lib_to_chars 202306L
# endif
// # define __cpp_lib_to_string 202306L
# undef __cpp_lib_tuple_like
// # define __cpp_lib_tuple_like 202311L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@
# error "__cpp_lib_constexpr_charconv should not be defined before c++23"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++17"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++17"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand All @@ -69,16 +69,16 @@
# error "__cpp_lib_constexpr_charconv should not be defined before c++23"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++20"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++20"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand All @@ -91,16 +91,16 @@
# error "__cpp_lib_constexpr_charconv should have the value 202207L in c++23"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++23"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++23"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand All @@ -113,16 +113,16 @@
# error "__cpp_lib_constexpr_charconv should have the value 202207L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++26"
# endif
# if __cpp_lib_to_chars != 202306L
# error "__cpp_lib_to_chars should have the value 202306L in c++26"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3081,16 +3081,16 @@
# error "__cpp_lib_to_array should not be defined before c++20"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++17"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++17"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand Down Expand Up @@ -4471,16 +4471,16 @@
# error "__cpp_lib_to_array should have the value 201907L in c++20"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++20"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++20"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand Down Expand Up @@ -6077,16 +6077,16 @@
# error "__cpp_lib_to_array should have the value 201907L in c++23"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++23"
# endif
# if __cpp_lib_to_chars != 201611L
# error "__cpp_lib_to_chars should have the value 201611L in c++23"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand Down Expand Up @@ -8007,16 +8007,16 @@
# error "__cpp_lib_to_array should have the value 201907L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
# ifndef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should be defined in c++26"
# endif
# if __cpp_lib_to_chars != 202306L
# error "__cpp_lib_to_chars should have the value 202306L in c++26"
# endif
# else // _LIBCPP_VERSION
# else
# ifdef __cpp_lib_to_chars
# error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
# error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
# endif
# endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
//
// from_chars_result from_chars(const char* first, const char* last,
// double& value, chars_format fmt = chars_format::general)
//
// from_chars_result from_chars(const char* first, const char* last,
// long double& value, chars_format fmt = chars_format::general)

#include <array>
#include <charconv>
Expand Down Expand Up @@ -1518,6 +1521,25 @@ struct test_hex {
// test/std/utilities/charconv/charconv.msvc/test.cpp
// uses random values. This tests contains errors found by this test.
void test_random_errors() {
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
{
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
// long double whose format is same as double.

long double value = 0.25L;
std::from_chars_result result = std::from_chars(s, last, value);

assert(result.ec == std::errc{});
assert(result.ptr == last);
assert(value == 4.219902180869891e-278L);
}
// TODO: Add more precise cases when the implementation for long double is complete.
#endif
{
const char* s = "4.219902180869891e-2788";
const char* last = s + std::strlen(s) - 1;
Expand Down
6 changes: 5 additions & 1 deletion libcxx/test/support/charconv_test_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,11 @@ auto all_unsigned = type_list<
>();
auto integrals = concat(all_signed, all_unsigned);

auto all_floats = type_list< float, double >(); //TODO: Add long double
#ifdef TEST_LONG_DOUBLE_IS_DOUBLE // TODO: Remove this condition when the implementation for long double is complete.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be defined in test_macros.h!

auto all_floats = type_list< float, double, long double >();
#else
auto all_floats = type_list< float, double >();
#endif

template <template <typename> class Fn, typename... Ts>
TEST_CONSTEXPR_CXX23 void
Expand Down
7 changes: 5 additions & 2 deletions libcxx/utils/generate_feature_test_macro_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -1333,7 +1333,8 @@ def add_version_header(tc):
"c++26": 202306, # P2497R0 Testing for success or failure of <charconv> functions
},
"headers": ["charconv"],
"unimplemented": True,
"test_suite_guard": "defined(TEST_LONG_DOUBLE_IS_DOUBLE)",
"libcxx_guard": "_LIBCPP_LONG_DOUBLE_IS_DOUBLE",
},
{
"name": "__cpp_lib_to_string",
Expand Down Expand Up @@ -1532,7 +1533,9 @@ def produce_macros_definition_for_std(std):
result += "# if %s\n" % tc["libcxx_guard"]
inner_indent += 2
if get_value_before(tc["values"], std) is not None:
assert "test_suite_guard" not in tc.keys()
# TRANSITION, __cpp_lib_to_chars has different values
# but needs to be guarded.
# assert "test_suite_guard" not in tc.keys()
result += "# undef %s\n" % tc["name"]
line = "#%sdefine %s" % ((" " * inner_indent), tc["name"])
line += " " * (indent - len(line))
Expand Down
Loading