From 2b9f7e6ed8e6daf6602088bd52196e51ee9f4172 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 27 Apr 2026 10:08:22 -0400 Subject: [PATCH 1/4] Add expanded reproducer --- test/Jamfile | 1 + test/github_issue_296.cpp | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 test/github_issue_296.cpp diff --git a/test/Jamfile b/test/Jamfile index e542ed77..8c6f6c98 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -75,3 +75,4 @@ run github_issue_267.cpp ; run github_issue_280.cpp ; run github_issue_282.cpp ; run github_issue_int128_320.cpp ; +run github_issue_296.cpp ; diff --git a/test/github_issue_296.cpp b/test/github_issue_296.cpp new file mode 100644 index 00000000..4c4e04aa --- /dev/null +++ b/test/github_issue_296.cpp @@ -0,0 +1,67 @@ +// Copyright 2026 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/296 + +#include +#include + +template +void test() +{ + const T value = -125.125; + + char buffer[64]; + const auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), value, boost::charconv::chars_format::fixed, 0); + BOOST_TEST(r); + *r.ptr = '\0'; + + BOOST_TEST_CSTR_EQ(buffer, "-125"); +} + +template +void test_pos() +{ + const T value = 125.125; + + char buffer[64]; + const auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), value, boost::charconv::chars_format::fixed, 0); + BOOST_TEST(r); + *r.ptr = '\0'; + + BOOST_TEST_CSTR_EQ(buffer, "125"); +} + +template +void test_prec_1() +{ + const T value = -125.125; + + char buffer[64]; + const auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), value, boost::charconv::chars_format::fixed, 1); + BOOST_TEST(r); + *r.ptr = '\0'; + + BOOST_TEST_CSTR_EQ(buffer, "-125.1"); +} + +int main() +{ + test(); + test(); + + test_pos(); + test_pos(); + + test_prec_1(); + test_prec_1(); + + #ifndef BOOST_CHARCONV_UNSUPPORTED_LONG_DOUBLE + test(); + test_pos(); + test_prec_1(); + #endif + + return boost::report_errors(); +} From 16864302b6456aab4f089d96cbe393c05b7d2030 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 27 Apr 2026 10:08:35 -0400 Subject: [PATCH 2/4] Fix buffer sizing calculation for precision 0 --- include/boost/charconv/detail/dragonbox/floff.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/charconv/detail/dragonbox/floff.hpp b/include/boost/charconv/detail/dragonbox/floff.hpp index 8eaaffd8..f67ce829 100644 --- a/include/boost/charconv/detail/dragonbox/floff.hpp +++ b/include/boost/charconv/detail/dragonbox/floff.hpp @@ -1764,7 +1764,7 @@ BOOST_CHARCONV_SAFEBUFFERS to_chars_result floff(const double x, int precision, const auto initial_digits = static_cast(prod >> 32); - buffer -= (initial_digits < 10 && buffer != first ? 1 : 0); + buffer -= (initial_digits < 10 && buffer != first && precision != 0 ? 1 : 0); remaining_digits -= (2 - (initial_digits < 10 ? 1 : 0)); // Avoid the situation where we have a leading 0 that we don't need From dd24281953fb1d69f934147862cc301123d5a6d2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 27 Apr 2026 10:22:11 -0400 Subject: [PATCH 3/4] Fix handling of negative precision 0 values with >=80 bit ldbls --- include/boost/charconv/detail/ryu/ryu_generic_128.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/boost/charconv/detail/ryu/ryu_generic_128.hpp b/include/boost/charconv/detail/ryu/ryu_generic_128.hpp index 2047d9a7..054039fe 100644 --- a/include/boost/charconv/detail/ryu/ryu_generic_128.hpp +++ b/include/boost/charconv/detail/ryu/ryu_generic_128.hpp @@ -436,14 +436,18 @@ static inline int generic_to_chars_fixed(const struct floating_decimal_128 v, ch { current_len = static_cast(shift_width) + precision; } + else + { + current_len = static_cast(shift_width) - 1; + } precision = 0; - // Since we wrote additional characters into the buffer we need to add a null terminator, + // Since we wrote additional characters into the buffer, we need to add a null terminator, // so they are not read const auto round_val = result[current_len]; result[current_len] = '\0'; - // More complicated rounding situations like 9999.999999 are already handled + // More complicated rounding situations like 9999.999999 are already handled, // so we don't need to worry about rounding past the decimal point if (round_val >= '5') { From 87bf578d1816fb37c733a245793e1b46ba37ab85 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 27 Apr 2026 12:09:54 -0400 Subject: [PATCH 4/4] Separate issue with decomposition of 128-bit long doubles --- test/github_issue_296.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/github_issue_296.cpp b/test/github_issue_296.cpp index 4c4e04aa..c75a5278 100644 --- a/test/github_issue_296.cpp +++ b/test/github_issue_296.cpp @@ -57,7 +57,7 @@ int main() test_prec_1(); test_prec_1(); - #ifndef BOOST_CHARCONV_UNSUPPORTED_LONG_DOUBLE + #if !defined(BOOST_CHARCONV_UNSUPPORTED_LONG_DOUBLE) && BOOST_CHARCONV_LDBL_BITS < 128 test(); test_pos(); test_prec_1();