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