Skip to content

Commit 227588c

Browse files
authored
Merge pull request #793 from cppalliance/to_chars_10s
Fix handling of trailing 0 in fixed format to_chars with unspecified precision
2 parents ee92e74 + dccbab7 commit 227588c

File tree

6 files changed

+85
-16
lines changed

6 files changed

+85
-16
lines changed

fuzzing/Jamfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ toolset.flags $(__name__).make-corpus PYTHON <python.interpreter> ;
3636

3737
for local fuzzer in $(all_fuzzers)
3838
{
39-
local fuzz_time = 60 ;
39+
local fuzz_time = 30 ;
4040
local corpus = /tmp/corpus/$(fuzzer) ;
4141
local min_corpus = /tmp/mincorpus/$(fuzzer) ;
4242
local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ;

include/boost/decimal/charconv.hpp

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -542,16 +542,16 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const
542542
{
543543
append_trailing_zeros = true;
544544
}
545+
}
545546

546-
// In general formatting we remove trailing 0s
547-
if (fmt == chars_format::general)
548-
{
549-
550-
const auto zeros_removal {remove_trailing_zeros(significand)};
551-
significand = zeros_removal.trimmed_number;
552-
exponent += static_cast<int>(zeros_removal.number_of_removed_zeros);
553-
num_dig -= static_cast<int>(zeros_removal.number_of_removed_zeros);
554-
}
547+
// In general formatting we remove trailing 0s
548+
// Same with unspecified precision fixed formatting
549+
if ((precision == -1 && fmt == chars_format::fixed) || fmt == chars_format::general)
550+
{
551+
const auto zeros_removal {remove_trailing_zeros(significand)};
552+
significand = zeros_removal.trimmed_number;
553+
exponent += static_cast<int>(zeros_removal.number_of_removed_zeros);
554+
num_dig -= static_cast<int>(zeros_removal.number_of_removed_zeros);
555555
}
556556

557557
// Make sure the result will fit in the buffer
@@ -569,25 +569,32 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const
569569
*first++ = '0';
570570
return {first, std::errc()};
571571
}
572-
else if (num_leading_zeros > precision)
572+
else if (precision != -1 && num_leading_zeros > precision)
573573
{
574574
*first++ = '0';
575575
*first++ = '.';
576-
std::memset(first, '0', static_cast<std::size_t>(precision));
576+
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(precision));
577577
return {first + precision, std::errc()};
578578
}
579579
else
580580
{
581581
*first++ = '0';
582582
*first++ = '.';
583-
std::memset(first, '0', static_cast<std::size_t>(num_leading_zeros));
583+
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(num_leading_zeros));
584584
first += num_leading_zeros;
585585

586586
// We can skip the rest if there's nothing more to do for the required precision
587587
if (significand == 0)
588588
{
589-
std::memset(first, '0', static_cast<std::size_t>(precision - num_leading_zeros));
590-
return {first + precision, std::errc()};
589+
if (precision - num_leading_zeros > 0)
590+
{
591+
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(precision - num_leading_zeros));
592+
return {first + precision, std::errc()};
593+
}
594+
else
595+
{
596+
return {first, std::errc()};
597+
}
591598
}
592599
}
593600
}

include/boost/decimal/detail/memcpy.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#if defined(__GNUC__) && __GNUC__ >= 10
2424
# pragma GCC diagnostic push
2525
# pragma GCC diagnostic ignored "-Wstringop-overflow"
26+
# pragma GCC diagnostic ignored "-Warray-bounds"
2627
# define BOOST_DECIMAL_STRINGOP_OVERFLOW_DISABLED
2728
#endif
2829

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ project : requirements
4545
run-fail benchmarks.cpp ;
4646
run compare_dec128_and_fast.cpp ;
4747
compile-fail concepts_test.cpp ;
48+
run crash_report_1.cpp ;
4849
run github_issue_426.cpp ;
4950
run github_issue_448.cpp ;
5051
run-fail github_issue_519.cpp ;

test/crash_report_1.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/decimal.hpp>
6+
#include <boost/core/lightweight_test.hpp>
7+
#include <iostream>
8+
9+
using namespace boost::decimal;
10+
11+
int main()
12+
{
13+
char buffer[64]{};
14+
const auto print_val {0.000001_df};
15+
16+
const auto r = boost::decimal::to_chars(
17+
buffer,
18+
buffer + sizeof(buffer),
19+
print_val,
20+
boost::decimal::chars_format::fixed
21+
);
22+
*r.ptr = '\0';
23+
24+
BOOST_TEST_CSTR_EQ(buffer, "0.000001");
25+
26+
return boost::report_errors();
27+
}

test/test_to_chars.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ static constexpr auto N = static_cast<std::size_t>(1024U >> 4U); // Number of tr
2323
#if !defined(BOOST_DECIMAL_DISABLE_CLIB) && !(defined(__GNUC__) && __GNUC__ >= 13 && !defined(__aarch64__))
2424

2525
template <typename T>
26-
void test_value(T val, const char* result, chars_format fmt, int precision = -1)
26+
void test_value(T val, const char* result, chars_format fmt, int precision)
2727
{
2828
char buffer[256] {};
2929
auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt, precision);
@@ -32,6 +32,16 @@ void test_value(T val, const char* result, chars_format fmt, int precision = -1)
3232
BOOST_TEST_CSTR_EQ(result, buffer);
3333
}
3434

35+
template <typename T>
36+
void test_value(T val, const char* result, chars_format fmt)
37+
{
38+
char buffer[256] {};
39+
auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt);
40+
*r.ptr = '\0';
41+
BOOST_TEST(r);
42+
BOOST_TEST_CSTR_EQ(result, buffer);
43+
}
44+
3545
template <typename T>
3646
void test_value(T val, const char* result)
3747
{
@@ -764,6 +774,25 @@ void test_777()
764774
test_value(value3, "-2111000000", chars_format::fixed, 0);
765775
}
766776

777+
template <typename T>
778+
void test_more_powers_10()
779+
{
780+
test_value(T{1, -6}, "0.000001", chars_format::fixed);
781+
test_value(T{1, -5}, "0.00001", chars_format::fixed);
782+
test_value(T{1, -4}, "0.0001", chars_format::fixed);
783+
test_value(T{1, -3}, "0.001", chars_format::fixed);
784+
test_value(T{1, -2}, "0.01", chars_format::fixed);
785+
test_value(T{1, -1}, "0.1", chars_format::fixed);
786+
test_value(T{1, 0}, "1", chars_format::fixed);
787+
test_value(T{1, 1}, "10", chars_format::fixed);
788+
test_value(T{1, 2}, "100", chars_format::fixed);
789+
test_value(T{1, 3}, "1000", chars_format::fixed);
790+
test_value(T{1, 4}, "10000", chars_format::fixed);
791+
test_value(T{1, 5}, "100000", chars_format::fixed);
792+
test_value(T{1, 6}, "1000000", chars_format::fixed);
793+
test_value(T{1, 7}, "10000000", chars_format::fixed);
794+
}
795+
767796
int main()
768797
{
769798
test_non_finite_values<decimal32>();
@@ -874,6 +903,10 @@ int main()
874903
test_777<decimal64_fast>();
875904
test_777<decimal128_fast>();
876905

906+
test_more_powers_10<decimal32>();
907+
test_more_powers_10<decimal64>();
908+
test_more_powers_10<decimal128>();
909+
877910
return boost::report_errors();
878911
}
879912

0 commit comments

Comments
 (0)