From 1ca5bb012fd155ac0d3ddfaafeeba5578059c9a2 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 8 Jun 2026 14:16:52 +0100 Subject: [PATCH] to string() does not handle floating point number with integer part exceeding max uint64 t (#1455) * Added 'scientific' flag to basic_format_spec to allow forcing of scientific format for all floating point values. * Forces floating point numbers that are larger than than the internal integral workspace types to be formatted in scientific format. Also allows scientific format to be forced for all floating point output. NAN and INF outputs now follow case settings, as does the new scientific exponent letter. 'e' or 'E'. * treefmt changes * Quick fixes * Removed * Added tests for NAN and INF Fixed result for -INF * Added tests for upper and lower case scintific format. * Changed incorrect stream header from to * Update test_to_string.cpp * Update test_to_u16string.cpp * Update test_to_u32string.cpp * Update test_to_u8string.cpp * Update test_to_wstring.cpp * clang-format updates * clang-format updates * Re-coded the precision calculation in add_floating_point_scientific to match add_floating_point_non_scientific * Rename step 1 * Fix: rename docs/Messaging to docs/messaging * Rename step 1 * Fix: rename docs/Messaging to docs/messaging * Rename step 1 * Fix: rename docs/Messaging to docs/messaging * Changed incorrect stream header from to * Update test_to_string.cpp * clang-format updates # Conflicts: # test/test_to_string.cpp * Remove old docs folder * clang-format updates # Conflicts: # test/test_to_string.cpp * Re-commit missing file --------- Co-authored-by: John Wellbelove --- .gitignore | 1 + include/etl/basic_format_spec.h | 35 ++++- include/etl/private/to_string_helper.h | 190 +++++++++++++++++++++---- test/test_to_string.cpp | 92 ++++++++++-- test/test_to_u16string.cpp | 84 +++++++++++ test/test_to_u32string.cpp | 83 +++++++++++ test/test_to_u8string.cpp | 84 +++++++++++ test/test_to_wstring.cpp | 83 +++++++++++ 8 files changed, 610 insertions(+), 42 deletions(-) diff --git a/.gitignore b/.gitignore index 33e67bd7..60d4e469 100644 --- a/.gitignore +++ b/.gitignore @@ -421,3 +421,4 @@ docs/*.html test/build-coverage include/etl/iterator.h~RFfd5eda.TMP test/vs2022/Build +docs/Messaging diff --git a/include/etl/basic_format_spec.h b/include/etl/basic_format_spec.h index 5c585d85..dc198f54 100644 --- a/include/etl/basic_format_spec.h +++ b/include/etl/basic_format_spec.h @@ -215,6 +215,7 @@ namespace etl , boolalpha_(false) , show_base_(false) , fill_(typename TString::value_type(' ')) + , scientific_(false) { } @@ -222,7 +223,7 @@ namespace etl /// Constructor. //*************************************************************************** ETL_CONSTEXPR basic_format_spec(uint_least8_t base__, uint_least8_t width__, uint_least8_t precision__, bool upper_case__, bool left_justified__, - bool boolalpha__, bool show_base__, typename TString::value_type fill__) ETL_NOEXCEPT + bool boolalpha__, bool show_base__, typename TString::value_type fill__, bool scientific__ = false) ETL_NOEXCEPT : base_(base__) , width_(width__) , precision_(precision__) @@ -231,6 +232,7 @@ namespace etl , boolalpha_(boolalpha__) , show_base_(show_base__) , fill_(fill__) + , scientific_(scientific__) { } @@ -246,6 +248,7 @@ namespace etl left_justified_ = false; boolalpha_ = false; show_base_ = false; + scientific_ = false; fill_ = typename TString::value_type(' '); } @@ -560,6 +563,33 @@ namespace etl return boolalpha_; } + //*************************************************************************** + /// Sets the scientific flag. + /// \return A reference to the basic_format_spec. + //*************************************************************************** + ETL_CONSTEXPR14 basic_format_spec& scientific(bool b) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + scientific_ = b; + return *this; + } + +#if ETL_USING_CPP11 + /// @overload + ETL_CONSTEXPR14 basic_format_spec&& scientific(bool b) ETL_RVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + scientific_ = b; + return etl::move(*this); + } +#endif + + //*************************************************************************** + /// Gets the scientific flag. + //*************************************************************************** + ETL_CONSTEXPR bool is_scientific() const ETL_NOEXCEPT + { + return scientific_; + } + //*************************************************************************** /// Equality operator. //*************************************************************************** @@ -567,7 +597,7 @@ namespace etl { return (lhs.base_ == rhs.base_) && (lhs.width_ == rhs.width_) && (lhs.precision_ == rhs.precision_) && (lhs.upper_case_ == rhs.upper_case_) && (lhs.left_justified_ == rhs.left_justified_) && (lhs.boolalpha_ == rhs.boolalpha_) && (lhs.show_base_ == rhs.show_base_) - && (lhs.fill_ == rhs.fill_); + && (lhs.fill_ == rhs.fill_) && (lhs.scientific_ == rhs.scientific_); } //*************************************************************************** @@ -588,6 +618,7 @@ namespace etl bool boolalpha_; bool show_base_; typename TString::value_type fill_; + bool scientific_; }; } // namespace etl diff --git a/include/etl/private/to_string_helper.h b/include/etl/private/to_string_helper.h index 1e1ff016..a8969eae 100644 --- a/include/etl/private/to_string_helper.h +++ b/include/etl/private/to_string_helper.h @@ -218,20 +218,42 @@ namespace etl /// Helper function for floating point nan and inf. //*************************************************************************** template - void add_nan_inf(const bool not_a_number, const bool infinity, TIString& str) + void add_nan_inf(const bool not_a_number, const bool infinity, const bool is_negative, TIString& str, + const etl::basic_format_spec& format) { typedef typename TIString::value_type type; - static const type n[] = {'n', 'a', 'n'}; - static const type i[] = {'i', 'n', 'f'}; + static const type nan_lower[] = {'n', 'a', 'n'}; + static const type nan_upper[] = {'N', 'A', 'N'}; + static const type inf_lower[] = {'i', 'n', 'f'}; + static const type inf_upper[] = {'I', 'N', 'F'}; if (not_a_number) { - str.insert(str.end(), ETL_OR_STD11::begin(n), ETL_OR_STD11::end(n)); + if (format.is_upper_case()) + { + str.insert(str.end(), ETL_OR_STD11::begin(nan_upper), ETL_OR_STD11::end(nan_upper)); + } + else + { + str.insert(str.end(), ETL_OR_STD11::begin(nan_lower), ETL_OR_STD11::end(nan_lower)); + } } else if (infinity) { - str.insert(str.end(), ETL_OR_STD11::begin(i), ETL_OR_STD11::end(i)); + if (is_negative) + { + str.push_back(type('-')); + } + + if (format.is_upper_case()) + { + str.insert(str.end(), ETL_OR_STD11::begin(inf_upper), ETL_OR_STD11::end(inf_upper)); + } + else + { + str.insert(str.end(), ETL_OR_STD11::begin(inf_lower), ETL_OR_STD11::end(inf_lower)); + } } } @@ -275,14 +297,137 @@ namespace etl } #endif + //*************************************************************************** + /// Helper function for floating point in scientific format. + //*************************************************************************** + template + void add_floating_point_scientific(const T value, TIString& str, const etl::basic_format_spec& format, const uint32_t max_precision) + { + typedef typename TIString::value_type type; + + const uint32_t requested_precision = format.get_precision(); + const uint32_t precision = (requested_precision > max_precision) ? max_precision : requested_precision; + + etl::basic_format_spec mantissa_integral_format = format; + mantissa_integral_format.decimal().width(0U).precision(0U); + + etl::basic_format_spec mantissa_fractional_format = mantissa_integral_format; + mantissa_fractional_format.precision(precision).width(precision).fill(type('0')).right(); + + T abs_value = etl::absolute(value); + + // Find exponent by iterative scaling + int32_t exponent = 0; + T scaled = abs_value; + + if (scaled >= T(1)) + { + // Scale down for values >= 1 + while (scaled >= T(10)) + { + scaled /= T(10); + ++exponent; + } + } + else if (scaled > T(0)) + { + // Scale up for values < 1 + while (scaled < T(1)) + { + scaled *= T(10); + --exponent; + } + } + + // Calculate the multiplier for the fractional part. + uworkspace_t multiplier = 1U; + for (uint32_t i = 0U; i < precision; ++i) + { + multiplier *= 10U; + } + + // Find the integral part of the floating point + T f_integral = ::floor(scaled); + uworkspace_t integral = static_cast(f_integral); + + // Find the fractional part of the floating point. + uworkspace_t fractional = static_cast(::round((scaled - f_integral) * multiplier)); + + // Check for a rounding carry to the integral. + if (fractional == multiplier) + { + ++integral; + fractional = 0U; + + if (integral == 10U) + { + integral = 1U; + ++exponent; + } + } + + etl::private_to_string::add_integral_and_fractional(integral, fractional, str, mantissa_integral_format, mantissa_fractional_format, + etl::is_negative(value)); + + // Append the exponent. + str.push_back(format.is_upper_case() ? type('E') : type('e')); + str.push_back((exponent < 0) ? type('-') : type('+')); + + uworkspace_t abs_exponent = static_cast(etl::absolute(exponent)); + + etl::basic_format_spec exponent_format = format; + exponent_format.decimal().width(1U).precision(0U).right(); + + etl::private_to_string::add_integral(abs_exponent, str, exponent_format, true, false); + } + + //*************************************************************************** + /// Helper function for floating point in non-scientific format. + //*************************************************************************** + template + void add_floating_point_non_scientific(const T value, TIString& str, const etl::basic_format_spec& format, const uint32_t max_precision) + { + typedef typename TIString::value_type type; + + etl::basic_format_spec integral_format = format; + integral_format.decimal().width(0).precision(format.get_precision() > max_precision ? max_precision : format.get_precision()); + + etl::basic_format_spec fractional_format = integral_format; + fractional_format.width(integral_format.get_precision()).fill(type('0')).right(); + + // Calculate the multiplier for the fractional part. + uworkspace_t multiplier = 1U; + + for (uint32_t i = 0U; i < fractional_format.get_precision(); ++i) + { + multiplier *= 10U; + } + + // Find the integral part of the floating point + T f_integral = ::floor(etl::absolute(value)); + uworkspace_t integral = static_cast(f_integral); + + // Find the fractional part of the floating point. + uworkspace_t fractional = static_cast(::round((etl::absolute(value) - f_integral) * multiplier)); + + // Check for a rounding carry to the integral. + if (fractional == multiplier) + { + ++integral; + fractional = 0U; + } + + // Create the string. + etl::private_to_string::add_integral_and_fractional(integral, fractional, str, integral_format, fractional_format, etl::is_negative(value)); + } + //*************************************************************************** /// Helper function for floating point. //*************************************************************************** template void add_floating_point(const T value, TIString& str, const etl::basic_format_spec& format, const bool append) { - typedef typename TIString::iterator iterator; - typedef typename TIString::value_type type; + typedef typename TIString::iterator iterator; if (!append) { @@ -293,7 +438,7 @@ namespace etl if (isnan(value) || isinf(value)) { - etl::private_to_string::add_nan_inf(isnan(value), isinf(value), str); + etl::private_to_string::add_nan_inf(isnan(value), isinf(value), etl::is_negative(value), str, format); } else { @@ -307,36 +452,19 @@ namespace etl } #endif - etl::basic_format_spec integral_format = format; - integral_format.decimal().width(0).precision(format.get_precision() > max_precision ? max_precision : format.get_precision()); + bool requires_scientific_form = format.is_scientific() || (etl::absolute(value) > static_cast(etl::numeric_limits::max())); - etl::basic_format_spec fractional_format = integral_format; - fractional_format.width(integral_format.get_precision()).fill(type('0')).right(); - - uworkspace_t multiplier = 1U; - - for (uint32_t i = 0U; i < fractional_format.get_precision(); ++i) + if (requires_scientific_form) { - multiplier *= 10U; + etl::private_to_string::add_floating_point_scientific(value, str, format, max_precision); } - - // Find the integral part of the floating point - T f_integral = ::floor(etl::absolute(value)); - uworkspace_t integral = static_cast(f_integral); - - // Find the fractional part of the floating point. - uworkspace_t fractional = static_cast(::round((etl::absolute(value) - f_integral) * multiplier)); - - // Check for a rounding carry to the integral. - if (fractional == multiplier) + else { - ++integral; - fractional = 0U; + etl::private_to_string::add_floating_point_non_scientific(value, str, format, max_precision); } - - etl::private_to_string::add_integral_and_fractional(integral, fractional, str, integral_format, fractional_format, etl::is_negative(value)); } + // Add alignment if necessary. etl::private_to_string::add_alignment(str, start, format); } diff --git a/test/test_to_string.cpp b/test/test_to_string.cpp index d9ef2919..1870697e 100644 --- a/test/test_to_string.cpp +++ b/test/test_to_string.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #include @@ -45,15 +46,6 @@ namespace SUITE(test_to_string) { - TEST(test_issue_314) - { - etl::string<20> str; - - etl::format_spec ft; - ft.precision(6); - etl::to_string(1.23, str, ft, true); - } - //************************************************************************* TEST(test_default_format_no_append) { @@ -675,5 +667,87 @@ namespace CHECK_EQUAL(etl::string<20>(STR("-124.0000")).c_str(), result_i.c_str()); CHECK_EQUAL(result_d.c_str(), result_i.c_str()); } + + //************************************************************************* + TEST(test_issue_1438_does_not_handle_floating_point_number_with_integer_part_exceeding_max_uint64_t) + { + if (std::numeric_limits::is_iec559) + { + // Check forced scientific formatting for smaller numbers. + etl::string<64> s0; + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); + CHECK(etl::string<64>(STR(" 1.00000e+3")) == s0); + + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::string<64>(STR(" 1.00000E+3")) == s0); + + // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. + etl::string<64> s1; + etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); + CHECK(etl::string<64>(STR(" 1.79769e+308")) == s1); + + // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. + etl::string<64> s2; + etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); + CHECK(etl::string<64>(STR(" -1.79769e+308")) == s2); + } + +#if ETL_USING_64BIT_TYPES + using workspace_t = etl::private_to_string::workspace_t; + + // Maximum workspace_t value is 9223372036854775807. + etl::string<64> s3; + etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); + CHECK(etl::string<64>(STR(" 9223372036854775807")) == s3); + + // Minimum workspace_t value is -9223372036854775808. + etl::string<64> s4; + etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); + CHECK(etl::string<64>(STR(" -9223372036854775808")) == s4); + + using uworkspace_t = etl::private_to_string::uworkspace_t; + + // Maximum uworkspace_t value is 18446744073709551615. + etl::string<64> s5; + etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); + CHECK(etl::string<64>(STR(" 18446744073709551615")) == s5); + + // Minimum uworkspace_t value is 0. + etl::string<64> s6; + etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); + CHECK(etl::string<64>(STR(" 0")) == s6); +#endif + } + + //************************************************************************* + TEST(test_issue_1436_case_support_for_nan_inf_in_to_string) + { + if (std::numeric_limits::is_iec559) + { + etl::string<64> s1; + etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::string<64>(STR(" nan")) == s1); + + etl::string<64> s2; + etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::string<64>(STR(" inf")) == s2); + + etl::string<64> s3; + etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::string<64>(STR(" -inf")) == s3); + + etl::string<64> s4; + etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::string<64>(STR(" NAN")) == s4); + + etl::string<64> s5; + etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::string<64>(STR(" INF")) == s5); + + etl::string<64> s6; + etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::string<64>(STR(" -INF")) == s6); + } + } } } // namespace diff --git a/test/test_to_u16string.cpp b/test/test_to_u16string.cpp index 4050b940..22a21c39 100644 --- a/test/test_to_u16string.cpp +++ b/test/test_to_u16string.cpp @@ -28,6 +28,8 @@ SOFTWARE. #include "unit_test_framework.h" +#include + #include "etl/format_spec.h" #include "etl/to_u16string.h" #include "etl/u16string.h" @@ -579,5 +581,87 @@ namespace CHECK(etl::u16string<20>(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } + + //************************************************************************* + TEST(test_issue_1438_does_not_handle_floating_point_number_with_integer_part_exceeding_max_uint64_t) + { + if (std::numeric_limits::is_iec559) + { + // Check forced scientific formatting for smaller numbers. + etl::u16string<64> s0; + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); + CHECK(etl::u16string<64>(STR(" 1.00000e+3")) == s0); + + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u16string<64>(STR(" 1.00000E+3")) == s0); + + // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. + etl::u16string<64> s1; + etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); + CHECK(etl::u16string<64>(STR(" 1.79769e+308")) == s1); + + // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. + etl::u16string<64> s2; + etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); + CHECK(etl::u16string<64>(STR(" -1.79769e+308")) == s2); + } + +#if ETL_USING_64BIT_TYPES + using workspace_t = etl::private_to_string::workspace_t; + + // Maximum workspace_t value is 9223372036854775807. + etl::u16string<64> s3; + etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); + CHECK(etl::u16string<64>(STR(" 9223372036854775807")) == s3); + + // Minimum workspace_t value is -9223372036854775808. + etl::u16string<64> s4; + etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); + CHECK(etl::u16string<64>(STR(" -9223372036854775808")) == s4); + + using uworkspace_t = etl::private_to_string::uworkspace_t; + + // Maximum uworkspace_t value is 18446744073709551615. + etl::u16string<64> s5; + etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); + CHECK(etl::u16string<64>(STR(" 18446744073709551615")) == s5); + + // Minimum uworkspace_t value is 0. + etl::u16string<64> s6; + etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); + CHECK(etl::u16string<64>(STR(" 0")) == s6); +#endif + } + + //************************************************************************* + TEST(test_issue_1436_case_support_for_nan_inf_in_to_string) + { + if (std::numeric_limits::is_iec559) + { + etl::u16string<64> s1; + etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u16string<64>(STR(" nan")) == s1); + + etl::u16string<64> s2; + etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u16string<64>(STR(" inf")) == s2); + + etl::u16string<64> s3; + etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u16string<64>(STR(" -inf")) == s3); + + etl::u16string<64> s4; + etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u16string<64>(STR(" NAN")) == s4); + + etl::u16string<64> s5; + etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u16string<64>(STR(" INF")) == s5); + + etl::u16string<64> s6; + etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u16string<64>(STR(" -INF")) == s6); + } + } } } // namespace diff --git a/test/test_to_u32string.cpp b/test/test_to_u32string.cpp index 9468ce45..523aa556 100644 --- a/test/test_to_u32string.cpp +++ b/test/test_to_u32string.cpp @@ -28,6 +28,7 @@ SOFTWARE. #include "unit_test_framework.h" +#include #include #include "etl/format_spec.h" @@ -582,5 +583,87 @@ namespace CHECK(etl::u32string<20>(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } + + //************************************************************************* + TEST(test_issue_1438_does_not_handle_floating_point_number_with_integer_part_exceeding_max_uint64_t) + { + if (std::numeric_limits::is_iec559) + { + // Check forced scientific formatting for smaller numbers. + etl::u32string<64> s0; + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); + CHECK(etl::u32string<64>(STR(" 1.00000e+3")) == s0); + + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u32string<64>(STR(" 1.00000E+3")) == s0); + + // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. + etl::u32string<64> s1; + etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); + CHECK(etl::u32string<64>(STR(" 1.79769e+308")) == s1); + + // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. + etl::u32string<64> s2; + etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); + CHECK(etl::u32string<64>(STR(" -1.79769e+308")) == s2); + } + +#if ETL_USING_64BIT_TYPES + using workspace_t = etl::private_to_string::workspace_t; + + // Maximum workspace_t value is 9223372036854775807. + etl::u32string<64> s3; + etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); + CHECK(etl::u32string<64>(STR(" 9223372036854775807")) == s3); + + // Minimum workspace_t value is -9223372036854775808. + etl::u32string<64> s4; + etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); + CHECK(etl::u32string<64>(STR(" -9223372036854775808")) == s4); + + using uworkspace_t = etl::private_to_string::uworkspace_t; + + // Maximum uworkspace_t value is 18446744073709551615. + etl::u32string<64> s5; + etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); + CHECK(etl::u32string<64>(STR(" 18446744073709551615")) == s5); + + // Minimum uworkspace_t value is 0. + etl::u32string<64> s6; + etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); + CHECK(etl::u32string<64>(STR(" 0")) == s6); +#endif + } + + //************************************************************************* + TEST(test_issue_1436_case_support_for_nan_inf_in_to_string) + { + if (std::numeric_limits::is_iec559) + { + etl::u32string<64> s1; + etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u32string<64>(STR(" nan")) == s1); + + etl::u32string<64> s2; + etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u32string<64>(STR(" inf")) == s2); + + etl::u32string<64> s3; + etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u32string<64>(STR(" -inf")) == s3); + + etl::u32string<64> s4; + etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u32string<64>(STR(" NAN")) == s4); + + etl::u32string<64> s5; + etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u32string<64>(STR(" INF")) == s5); + + etl::u32string<64> s6; + etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u32string<64>(STR(" -INF")) == s6); + } + } } } // namespace diff --git a/test/test_to_u8string.cpp b/test/test_to_u8string.cpp index 8f2c62e3..a1d4eece 100644 --- a/test/test_to_u8string.cpp +++ b/test/test_to_u8string.cpp @@ -28,6 +28,8 @@ SOFTWARE. #include "unit_test_framework.h" +#include + #include "etl/format_spec.h" #include "etl/to_u8string.h" #include "etl/u8string.h" @@ -581,6 +583,88 @@ namespace CHECK(etl::u8string<20>(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } + + //************************************************************************* + TEST(test_issue_1438_does_not_handle_floating_point_number_with_integer_part_exceeding_max_uint64_t) + { + if (std::numeric_limits::is_iec559) + { + // Check forced scientific formatting for smaller numbers. + etl::u8string<64> s0; + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); + CHECK(etl::u8string<64>(STR(" 1.00000e+3")) == s0); + + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u8string<64>(STR(" 1.00000E+3")) == s0); + + // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. + etl::u8string<64> s1; + etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); + CHECK(etl::u8string<64>(STR(" 1.79769e+308")) == s1); + + // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. + etl::u8string<64> s2; + etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); + CHECK(etl::u8string<64>(STR(" -1.79769e+308")) == s2); + } + + #if ETL_USING_64BIT_TYPES + using workspace_t = etl::private_to_string::workspace_t; + + // Maximum workspace_t value is 9223372036854775807. + etl::u8string<64> s3; + etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); + CHECK(etl::u8string<64>(STR(" 9223372036854775807")) == s3); + + // Minimum workspace_t value is -9223372036854775808. + etl::u8string<64> s4; + etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); + CHECK(etl::u8string<64>(STR(" -9223372036854775808")) == s4); + + using uworkspace_t = etl::private_to_string::uworkspace_t; + + // Maximum uworkspace_t value is 18446744073709551615. + etl::u8string<64> s5; + etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); + CHECK(etl::u8string<64>(STR(" 18446744073709551615")) == s5); + + // Minimum uworkspace_t value is 0. + etl::u8string<64> s6; + etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); + CHECK(etl::u8string<64>(STR(" 0")) == s6); + #endif + } + + //************************************************************************* + TEST(test_issue_1436_case_support_for_nan_inf_in_to_string) + { + if (std::numeric_limits::is_iec559) + { + etl::u8string<64> s1; + etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u8string<64>(STR(" nan")) == s1); + + etl::u8string<64> s2; + etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u8string<64>(STR(" inf")) == s2); + + etl::u8string<64> s3; + etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::u8string<64>(STR(" -inf")) == s3); + + etl::u8string<64> s4; + etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u8string<64>(STR(" NAN")) == s4); + + etl::u8string<64> s5; + etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u8string<64>(STR(" INF")) == s5); + + etl::u8string<64> s6; + etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::u8string<64>(STR(" -INF")) == s6); + } + } } } // namespace diff --git a/test/test_to_wstring.cpp b/test/test_to_wstring.cpp index 0c9d6fc3..feb3b9cd 100644 --- a/test/test_to_wstring.cpp +++ b/test/test_to_wstring.cpp @@ -29,6 +29,7 @@ SOFTWARE. #include "unit_test_framework.h" #include +#include #include #include @@ -645,5 +646,87 @@ namespace CHECK(etl::wstring<20>(STR("-124.0000")) == result_i); CHECK(result_d == result_i); } + + //************************************************************************* + TEST(test_issue_1438_does_not_handle_floating_point_number_with_integer_part_exceeding_max_uint64_t) + { + if (std::numeric_limits::is_iec559) + { + // Check forced scientific formatting for smaller numbers. + etl::wstring<64> s0; + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true)); + CHECK(etl::wstring<64>(STR(" 1.00000e+3")) == s0); + + etl::to_string(1000.0, s0, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::wstring<64>(STR(" 1.00000E+3")) == s0); + + // Maximum double value is 1.7976931348623157e+308, which rounds to 1.79769e+308 with 5 digits of precision. + etl::wstring<64> s1; + etl::to_string(std::numeric_limits::max(), s1, Format().precision(5).width(15).right()); + CHECK(etl::wstring<64>(STR(" 1.79769e+308")) == s1); + + // Negative maximum double value is -1.7976931348623157e+308, which rounds to -1.79769e+308 with 5 digits of precision. + etl::wstring<64> s2; + etl::to_string(-std::numeric_limits::max(), s2, Format().precision(5).width(15).right()); + CHECK(etl::wstring<64>(STR(" -1.79769e+308")) == s2); + } + +#if ETL_USING_64BIT_TYPES + using workspace_t = etl::private_to_string::workspace_t; + + // Maximum workspace_t value is 9223372036854775807. + etl::wstring<64> s3; + etl::to_string(std::numeric_limits::max(), s3, Format().precision(5).width(21).right()); + CHECK(etl::wstring<64>(STR(" 9223372036854775807")) == s3); + + // Minimum workspace_t value is -9223372036854775808. + etl::wstring<64> s4; + etl::to_string(std::numeric_limits::min(), s4, Format().precision(5).width(21).right()); + CHECK(etl::wstring<64>(STR(" -9223372036854775808")) == s4); + + using uworkspace_t = etl::private_to_string::uworkspace_t; + + // Maximum uworkspace_t value is 18446744073709551615. + etl::wstring<64> s5; + etl::to_string(std::numeric_limits::max(), s5, Format().precision(5).width(21).right()); + CHECK(etl::wstring<64>(STR(" 18446744073709551615")) == s5); + + // Minimum uworkspace_t value is 0. + etl::wstring<64> s6; + etl::to_string(std::numeric_limits::min(), s6, Format().precision(5).width(21).right()); + CHECK(etl::wstring<64>(STR(" 0")) == s6); +#endif + } + + //************************************************************************* + TEST(test_issue_1436_case_support_for_nan_inf_in_to_string) + { + if (std::numeric_limits::is_iec559) + { + etl::wstring<64> s1; + etl::to_string(std::numeric_limits::quiet_NaN(), s1, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::wstring<64>(STR(" nan")) == s1); + + etl::wstring<64> s2; + etl::to_string(std::numeric_limits::infinity(), s2, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::wstring<64>(STR(" inf")) == s2); + + etl::wstring<64> s3; + etl::to_string(-std::numeric_limits::infinity(), s3, Format().precision(5).width(15).right().scientific(true).upper_case(false)); + CHECK(etl::wstring<64>(STR(" -inf")) == s3); + + etl::wstring<64> s4; + etl::to_string(std::numeric_limits::quiet_NaN(), s4, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::wstring<64>(STR(" NAN")) == s4); + + etl::wstring<64> s5; + etl::to_string(std::numeric_limits::infinity(), s5, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::wstring<64>(STR(" INF")) == s5); + + etl::wstring<64> s6; + etl::to_string(-std::numeric_limits::infinity(), s6, Format().precision(5).width(15).right().scientific(true).upper_case(true)); + CHECK(etl::wstring<64>(STR(" -INF")) == s6); + } + } } } // namespace