diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2023-08-16 16:55:00 +0100 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2023-08-17 20:24:17 +0100 |
commit | aeed687f4e08f1364ab60882a012fe740e05a7da (patch) | |
tree | cc479e8a6cb79a46f120000175c971d6c50fc068 /libstdc++-v3/testsuite/21_strings | |
parent | 51ec07b116b802d845d762beddf8b98e8c0e32ab (diff) | |
download | gcc-aeed687f4e08f1364ab60882a012fe740e05a7da.zip gcc-aeed687f4e08f1364ab60882a012fe740e05a7da.tar.gz gcc-aeed687f4e08f1364ab60882a012fe740e05a7da.tar.bz2 |
libstdc++: Implement std::to_string in terms of std::format (P2587R3)
This change for C++26 affects std::to_string for floating-point
arguments, so that they should be formatted using std::format("{}", v)
instead of using sprintf. The modified specification in the standard
also affects integral arguments, but there's no observable difference
for them, and we already use std::to_chars for them anyway.
To avoid <string> depending on all of <format>, this change actually
just uses std::to_chars directly instead of using std::format. This is
equivalent, because the format spec "{}" doesn't use any of the other
features of std::format.
libstdc++-v3/ChangeLog:
* include/bits/basic_string.h (to_string(floating-point-type)):
Implement using std::to_chars for C++26.
* include/bits/version.def (__cpp_lib_to_string): Define.
* include/bits/version.h: Regenerate.
* testsuite/21_strings/basic_string/numeric_conversions/char/dr1261.cc:
Adjust expected result in C++26 mode.
* testsuite/21_strings/basic_string/numeric_conversions/char/to_string.cc:
Likewise.
* testsuite/21_strings/basic_string/numeric_conversions/wchar_t/dr1261.cc:
Likewise.
* testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring.cc:
Likewise.
* testsuite/21_strings/basic_string/numeric_conversions/char/to_string_float.cc:
New test.
* testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_float.cc:
New test.
* testsuite/21_strings/basic_string/numeric_conversions/version.cc:
New test.
Diffstat (limited to 'libstdc++-v3/testsuite/21_strings')
7 files changed, 341 insertions, 10 deletions
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/dr1261.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/dr1261.cc index 49998b5..b952c26 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/dr1261.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/dr1261.cc @@ -46,14 +46,19 @@ void test01() const string six(to_string(400ull)); VERIFY( six == "400" ); + string tail; +#if __cpp_lib_to_string < 202306L + tail = ".000000"; +#endif + const string seven(to_string(-1.0F)); - VERIFY( seven == "-1.000000" ); + VERIFY( seven == "-1" + tail ); const string eight(to_string(2.0)); - VERIFY( eight == "2.000000" ); + VERIFY( eight == "2" + tail ); const string nine(to_string(-4.0L)); - VERIFY( nine == "-4.000000" ); + VERIFY( nine == "-4" + tail ); } int main() diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string.cc index dc7b87b..c770b4f 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string.cc @@ -46,13 +46,18 @@ test01() string four(to_string(ull2)); VERIFY( four == "3000" ); + string tail; +#if __cpp_lib_to_string < 202306L + tail = ".000000"; +#endif + long double ld1 = 2.0L; string five(to_string(ld1)); - VERIFY( five == "2.000000" ); + VERIFY( five == "2" + tail ); long double ld2 = -4.0L; string six(to_string(ld2)); - VERIFY( six == "-4.000000" ); + VERIFY( six == "-4" + tail ); } int main() diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_float.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_float.cc new file mode 100644 index 0000000..3837c89 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_float.cc @@ -0,0 +1,148 @@ +// { dg-do run { target c++11 } } +// { dg-require-namedlocale "de_DE.ISO8859-15" } + +// C++11 21.5 Numeric Conversions [string.conversions] + +#include <string> +#include <format> +#include <limits> +#include <locale> +#include <cstdio> +#include <testsuite_hooks.h> + +namespace test +{ +// Canonical version of std::to_string(double) as specified in the standard. + +#if __cplusplus > 202302L + +#ifndef __cpp_lib_to_string +# error "Feature-test macro for std::to_string missing in <string>" +#elif __cpp_lib_to_string != 202306L +# error "Feature-test macro for std::to_string has wrong value in <string>" +#endif + +static std::string to_string(float val) { return std::format("{}", val); } +static std::string to_string(double val) { return std::format("{}", val); } +static std::string to_string(long double val) { return std::format("{}", val); } + +#else + +#ifdef __cpp_lib_to_string +# error "__cpp_lib_to_string should not be defined for C++23" +#endif + +static std::string to_string(double val) +{ + std::string str(100, '9'); +retry: + const int size = str.size(); + const int len = std::snprintf(&str[0], size + 1, "%f", val); + str.resize(len); + if (len > size) + goto retry; + return str; +} + +// snprintf promotes float to double +static std::string to_string(float val) { return to_string((double)val); } + +static std::string to_string(long double val) +{ + std::string str(100, '9'); +retry: + const int size = str.size(); + const int len = std::snprintf(&str[0], size + 1, "%Lf", val); + str.resize(len); + if (len > size) + goto retry; + return str; +} +#endif +} // namespace test + +template<typename T> + void check_value(T val) + { + const std::string s = std::to_string(val); + const std::string expected = test::to_string(val); + VERIFY( s == expected ); + VERIFY( s[s.size()] == '\0' ); // null-terminator not overwritten + } + +template<typename T> + void check_values() + { + const T values[] = { + 0.0, 0.0625, 0.25, 0.5, 1.25, 1e2, 1e7, 1e8, 1e-2, 1e-7, 1e-8, + 2e38, 4.4e+19, 6.25e-12, 7.89e+23, + 12345.6789, (T) 1234567890123456.e100L, (T) 1213141516e-99L, + std::numeric_limits<T>::min(), + std::numeric_limits<T>::max(), + std::numeric_limits<T>::epsilon(), + std::numeric_limits<T>::infinity(), + std::numeric_limits<T>::quiet_NaN(), + }; + + std::locale::global(std::locale::classic()); + + for (auto v : values) + { + check_value(v); + check_value(-v); + } + + std::locale::global(std::locale(ISO_8859(15,de_DE))); + + for (auto v : values) + { + check_value(v); + check_value(-v); + } + + std::locale::global(std::locale::classic()); + } + +void test01() +{ + // Examples from P2587R3 `to_string` or not `to_string` + + + VERIFY( std::to_string(42) == "42" ); + VERIFY( std::to_string(12345) == "12345" ); + auto max = std::to_string(1.7976931348623157e+308); + +#if __cplusplus <= 202302L + VERIFY( std::to_string(0.42) == "0.420000" ); + VERIFY( std::to_string(1e-7) == "0.000000" ); + VERIFY( std::to_string(-1e-7) == "-0.000000" ); + VERIFY( max.substr(0, 17) == "17976931348623157" ); + VERIFY( max.substr(max.size() - 7) == ".000000" ); +#else + VERIFY( std::to_string(0.42) == "0.42" ); + VERIFY( std::to_string(1e-7) == "1e-07" ); + VERIFY( std::to_string(-1e-7) == "-1e-07" ); + VERIFY( max == "1.7976931348623157e+308" ); +#endif + + std::locale::global(std::locale(ISO_8859(15,de_DE))); +#if __cplusplus <= 202302L + VERIFY( std::to_string(1234.5) == "1234,500000" ); +#else + VERIFY( std::to_string(1234.5) == "1234.5" ); +#endif + std::locale::global(std::locale::classic()); +} + +void test02() +{ + check_values<float>(); + check_values<double>(); + check_values<long double>(); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/version.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/version.cc new file mode 100644 index 0000000..630e06f --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/version.cc @@ -0,0 +1,18 @@ +// { dg-do compile } +#include <version> + +#if __cplusplus > 202302L + +#ifndef __cpp_lib_to_string +# error "Feature-test macro for std::to_string missing in <string>" +#elif __cpp_lib_to_string != 202306L +# error "Feature-test macro for std::to_string has wrong value in <string>" +#endif + +#else + +#ifdef __cpp_lib_to_string +# error "__cpp_lib_to_string should not be defined for C++23" +#endif + +#endif diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/dr1261.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/dr1261.cc index 8dbdd7b..c2b36fd 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/dr1261.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/dr1261.cc @@ -46,14 +46,19 @@ void test01() const wstring six(to_wstring(400ull)); VERIFY( six == L"400" ); + wstring tail; +#if __cpp_lib_to_string < 202306L + tail = L".000000"; +#endif + const wstring seven(to_wstring(-1.0F)); - VERIFY( seven == L"-1.000000" ); + VERIFY( seven == L"-1" + tail ); const wstring eight(to_wstring(2.0)); - VERIFY( eight == L"2.000000" ); + VERIFY( eight == L"2" + tail ); const wstring nine(to_wstring(-4.0L)); - VERIFY( nine == L"-4.000000" ); + VERIFY( nine == L"-4" + tail ); } int main() diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring.cc index dbfb639..6698783 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring.cc @@ -47,13 +47,18 @@ test01() wstring four(to_wstring(ull2)); VERIFY( four == L"3000" ); + wstring tail; +#if __cpp_lib_to_string < 202306L + tail = L".000000"; +#endif + long double ld1 = 2.0L; wstring five(to_wstring(ld1)); - VERIFY( five == L"2.000000" ); + VERIFY( five == L"2" + tail ); long double ld2 = -4.0L; wstring six(to_wstring(ld2)); - VERIFY( six == L"-4.000000" ); + VERIFY( six == L"-4" + tail ); #endif } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_float.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_float.cc new file mode 100644 index 0000000..83f5bb1 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_float.cc @@ -0,0 +1,145 @@ +// { dg-do run { target c++11 } } +// { dg-require-namedlocale "de_DE.ISO8859-15" } +// { dg-require-string-conversions "" } + +// C++11 21.5 Numeric Conversions [string.conversions] + +#include <string> +#include <format> +#include <limits> +#include <locale> +#include <cstdio> +#include <testsuite_hooks.h> + +namespace test +{ +// Canonical version of std::to_wstring(double) as specified in the standard. + +#if __cplusplus > 202302L + +std::wstring to_wstring(float val) { return std::format(L"{}", val); } +std::wstring to_wstring(double val) { return std::format(L"{}", val); } +std::wstring to_wstring(long double val) { return std::format(L"{}", val); } + +#else + +std::wstring to_wstring(double val) +{ + std::wstring str(100, L'9'); +retry: + const int size = str.size(); + const int len = std::swprintf(&str[0], size + 1, L"%f", val); + if (len == -1) // N.B. swprintf just returns -1 if the buffer is too small. + { + str.resize(size * 2); + goto retry; + } + str.resize(len); + return str; +} + +// snprintf promotes float to double +std::wstring to_wstring(float val) { return to_wstring((double)val); } + +std::wstring to_wstring(long double val) +{ + std::wstring str(100, L'9'); +retry: + const int size = str.size(); + const int len = std::swprintf(&str[0], size + 1, L"%Lf", val); + if (len == -1) // N.B. swprintf just returns -1 if the buffer is too small. + { + str.resize(size * 2); + goto retry; + } + str.resize(len); + return str; +} +#endif +} // namespace test + +template<typename T> + void check_value(T val) + { + const std::wstring s = std::to_wstring(val); + const std::wstring expected = test::to_wstring(val); + VERIFY( s == expected ); + VERIFY( s[s.size()] == L'\0' ); // null-terminator not overwritten + } + +template<typename T> + void check_values() + { + const T values[] = { + 0.0, 0.0625, 0.25, 0.5, 1.25, 1e2, 1e7, 1e8, 1e-2, 1e-7, 1e-8, + 2e38, 4.4e+19, 6.25e-12, 7.89e+23, + 12345.6789, (T) 1234567890123456.e100L, (T) 1213141516e-99L, + std::numeric_limits<T>::min(), + std::numeric_limits<T>::max(), + std::numeric_limits<T>::epsilon(), + std::numeric_limits<T>::infinity(), + std::numeric_limits<T>::quiet_NaN(), + }; + + std::locale::global(std::locale::classic()); + + for (auto v : values) + { + check_value(v); + check_value(-v); + } + + std::locale::global(std::locale(ISO_8859(15,de_DE))); + + for (auto v : values) + { + check_value(v); + check_value(-v); + } + + std::locale::global(std::locale::classic()); + } + +void test01() +{ + // Examples from P2587R3 `to_string` or not `to_string` + + + VERIFY( std::to_wstring(42) == L"42" ); + VERIFY( std::to_wstring(12345) == L"12345" ); + auto max = std::to_wstring(1.7976931348623157e+308); + +#if __cplusplus <= 202302L + VERIFY( std::to_wstring(0.42) == L"0.420000" ); + VERIFY( std::to_wstring(1e-7) == L"0.000000" ); + VERIFY( std::to_wstring(-1e-7) == L"-0.000000" ); + VERIFY( max.substr(0, 17) == L"17976931348623157" ); + VERIFY( max.substr(max.size() - 7) == L".000000" ); +#else + VERIFY( std::to_wstring(0.42) == L"0.42" ); + VERIFY( std::to_wstring(1e-7) == L"1e-07" ); + VERIFY( std::to_wstring(-1e-7) == L"-1e-07" ); + VERIFY( max == L"1.7976931348623157e+308" ); +#endif + + std::locale::global(std::locale(ISO_8859(15,de_DE))); +#if __cplusplus <= 202302L + VERIFY( std::to_wstring(1234.5) == L"1234,500000" ); +#else + VERIFY( std::to_wstring(1234.5) == L"1234.5" ); +#endif + std::locale::global(std::locale::classic()); +} + +void test02() +{ + check_values<float>(); + check_values<double>(); + check_values<long double>(); +} + +int main() +{ + test01(); + test02(); +} |