diff --git a/src/to_chars.cpp b/src/to_chars.cpp index 24ddf0c..0bdde32 100644 --- a/src/to_chars.cpp +++ b/src/to_chars.cpp @@ -313,12 +313,6 @@ namespace boost { namespace charconv { namespace detail { namespace to_chars_det { auto buffer = first; - const std::ptrdiff_t total_length = total_buffer_length(17, exponent, false); - if (total_length > (last - first)) - { - return {last, std::errc::value_too_large}; - } - // Print significand by decomposing it into a 9-digit block and a 8-digit block. std::uint32_t first_block; std::uint32_t second_block {}; @@ -337,6 +331,12 @@ namespace boost { namespace charconv { namespace detail { namespace to_chars_det no_second_block = true; } + const std::ptrdiff_t total_length = total_buffer_length(no_second_block ? 9 : 17, exponent, false);; + if (total_length > (last - first)) + { + return {last, std::errc::value_too_large}; + } + if (no_second_block) { print_9_digits(first_block, exponent, buffer); diff --git a/src/to_chars_float_impl.hpp b/src/to_chars_float_impl.hpp index d1be972..916d0c4 100644 --- a/src/to_chars_float_impl.hpp +++ b/src/to_chars_float_impl.hpp @@ -680,6 +680,7 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f auto abs_value = std::abs(value); constexpr auto max_fractional_value = std::is_same::value ? static_cast(1e16) : static_cast(1e7); + constexpr auto min_fractional_value = static_cast(1) / static_cast(100000); // 1e-1 takes more characters than 0.1 constexpr auto max_value = static_cast((std::numeric_limits::max)()); // Unspecified precision so we always go with the shortest representation @@ -687,7 +688,7 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f { if (fmt == boost::charconv::chars_format::general) { - if (abs_value >= 1 && abs_value < max_fractional_value) + if (abs_value > min_fractional_value && abs_value < max_fractional_value) { return to_chars_fixed_impl(first, last, value, fmt, precision); } diff --git a/test/Jamfile b/test/Jamfile index 246b208..57f19c5 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -73,3 +73,4 @@ run github_issue_212.cpp ; run github_issue_266.cpp ; run github_issue_267.cpp ; run github_issue_280.cpp ; +run github_issue_282.cpp ; diff --git a/test/github_issue_282.cpp b/test/github_issue_282.cpp new file mode 100644 index 0000000..17383e6 --- /dev/null +++ b/test/github_issue_282.cpp @@ -0,0 +1,79 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// See: https://github.com/boostorg/charconv/issues/282 + +#include +#include + +template +void test_no_format() +{ + char buffer[size]; + const auto r = boost::charconv::to_chars(buffer, buffer + size, 0.1); + if (BOOST_TEST(r)) + { + *r.ptr = '\0'; + BOOST_TEST_CSTR_EQ(buffer, "0.1"); + } +} + +template +void test_with_format() +{ + char buffer[size]; + const auto r = boost::charconv::to_chars(buffer, buffer + size, 0.1, boost::charconv::chars_format::general); + if (BOOST_TEST(r)) + { + *r.ptr = '\0'; + BOOST_TEST_CSTR_EQ(buffer, "0.1"); + } +} + +template +void test_smaller(const double value, const char* res) +{ + char buffer[size]; + const auto r = boost::charconv::to_chars(buffer, buffer + size, value, boost::charconv::chars_format::general); + if (BOOST_TEST(r)) + { + *r.ptr = '\0'; + BOOST_TEST_CSTR_EQ(buffer, res); + } +} + +int main() +{ + test_no_format<20>(); + test_no_format<100>(); + test_no_format::max_chars10>(); + + test_with_format<20>(); + test_with_format<100>(); + test_with_format::max_chars10>(); + + // The following match the behavior of GCC 15.2 + + // 0.01 vs 1e-02 + test_smaller<20>(0.01, "0.01"); + test_smaller<100>(0.01, "0.01"); + test_smaller::max_chars10>(0.01, "0.01"); + + // 0.001 vs 1e-03 + test_smaller<20>(0.001, "0.001"); + test_smaller<100>(0.001, "0.001"); + test_smaller::max_chars10>(0.001, "0.001"); + + // 0.0001 vs 1e-04 + test_smaller<20>(0.0001, "0.0001"); + test_smaller<100>(0.0001, "0.0001"); + test_smaller::max_chars10>(0.0001, "0.0001"); + + // 0.00001 vs 1e-05 + test_smaller<20>(0.00001, "1e-05"); + test_smaller<100>(0.00001, "1e-05"); + test_smaller::max_chars10>(0.00001, "1e-05"); + + return boost::report_errors(); +}