Index: libcxx/src/string.cpp =================================================================== --- libcxx/src/string.cpp +++ libcxx/src/string.cpp @@ -348,14 +348,63 @@ namespace { -// as_string +// f_to_string -template +template +struct value_traits; + +template<> +struct value_traits +{ + static constexpr size_t stack_buffer_size = 47; +}; + +template<> +struct value_traits +{ + static constexpr size_t stack_buffer_size = 317; +}; + +template<> +struct value_traits +{ + // For performance reasons value 4941 would be a better choice for 16-bit + // long double but in this way to_wstring(long double) would require 16Kb + // of stack memory. Use the same value as for doubles as tradeoff between + // performance and stack memory usage. + static constexpr size_t stack_buffer_size = 317; +}; + +template inline S -as_string(P sprintf_like, S s, const typename S::value_type* fmt, V a) +f_to_string(P sprintf_like, const typename S::value_type* fmt, V a) { typedef typename S::size_type size_type; + + S s; + + // Try fast algorithm with stack buffer. Return result on success or resize + // result to advice on characters count on fail. + constexpr size_t buf_size = value_traits::stack_buffer_size + 1; + typename S::value_type buf[buf_size]; + int status = sprintf_like(buf, buf_size, fmt, a); + if (status >= 0) + { + size_type used = static_cast(status); + if (used <= buf_size) + { + s.assign(buf, buf + used); + return s; + } + s.resize(used); + } + else + { + s.resize(2 * buf_size + 1); + } + + // Fallback to iterative version with growing capacity. size_type available = s.size(); while (true) { @@ -377,33 +426,6 @@ return s; } -template -struct initial_string; - -template <> -struct initial_string -{ - string - operator()() const - { - string s; - s.resize(s.capacity()); - return s; - } -}; - -template <> -struct initial_string -{ - wstring - operator()() const - { - wstring s(20, wchar_t()); - s.resize(s.capacity()); - return s; - } -}; - typedef int (*wide_printf)(wchar_t* __restrict, size_t, const wchar_t*__restrict, ...); inline @@ -447,12 +469,12 @@ wstring to_wstring(unsigned long long val) { return i_to_string(val); } -string to_string (float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); } -string to_string (double val) { return as_string(snprintf, initial_string< string>()(), "%f", val); } -string to_string (long double val) { return as_string(snprintf, initial_string< string>()(), "%Lf", val); } +string to_string (float val) { return f_to_string< string>(snprintf, "%f", val); } +string to_string (double val) { return f_to_string< string>(snprintf, "%f", val); } +string to_string (long double val) { return f_to_string< string>(snprintf, "%Lf", val); } -wstring to_wstring(float val) { return as_string(get_swprintf(), initial_string()(), L"%f", val); } -wstring to_wstring(double val) { return as_string(get_swprintf(), initial_string()(), L"%f", val); } -wstring to_wstring(long double val) { return as_string(get_swprintf(), initial_string()(), L"%Lf", val); } +wstring to_wstring(float val) { return f_to_string(get_swprintf(), L"%f", val); } +wstring to_wstring(double val) { return f_to_string(get_swprintf(), L"%f", val); } +wstring to_wstring(long double val) { return f_to_string(get_swprintf(), L"%Lf", val); } _LIBCPP_END_NAMESPACE_STD Index: libcxx/test/std/strings/string.conversions/to_string.pass.cpp =================================================================== --- libcxx/test/std/strings/string.conversions/to_string.pass.cpp +++ libcxx/test/std/strings/string.conversions/to_string.pass.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "test_macros.h" @@ -112,6 +113,19 @@ assert(s[s.size()] == 0); assert(s == "-12345.000000"); } + + // to_string is required to be result of printf-like function with %f / + // %Lf format specifier. %f and %Lf format specifiers convert floating + // point number to string representation with minimum number of digits + // after decimal point character equal to 6. + const auto check_by_deconversion = [](const T value){ + const std::string s = std::to_string(value); + const T restored_value = static_cast(std::stold(s)); + assert(std::fabs(value - restored_value) < T(1e-6)); + }; + check_by_deconversion(std::numeric_limits::max()); + check_by_deconversion(std::numeric_limits::min()); + check_by_deconversion(std::numeric_limits::lowest()); } int main(int, char**) Index: libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp =================================================================== --- libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp +++ libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "test_macros.h" @@ -112,6 +113,19 @@ assert(s[s.size()] == 0); assert(s == L"-12345.000000"); } + + // to_wstring is required to be result of printf-like function with %f / + // %Lf format specifier. %f and %Lf format specifiers convert floating + // point number to string representation with minimum number of digits + // after decimal point character equal to 6. + const auto check_by_deconversion = [](const T value){ + const std::wstring s = std::to_wstring(value); + const T restored_value = static_cast(std::stold(s)); + assert(std::fabs(value - restored_value) < T(1e-6)); + }; + check_by_deconversion(std::numeric_limits::max()); + check_by_deconversion(std::numeric_limits::min()); + check_by_deconversion(std::numeric_limits::lowest()); } int main(int, char**)