Skip to content

Commit

Permalink
Merge pull request #793 from cppalliance/to_chars_10s
Browse files Browse the repository at this point in the history
Fix handling of trailing 0 in fixed format to_chars with unspecified precision
  • Loading branch information
mborland authored Jan 18, 2025
2 parents ee92e74 + dccbab7 commit 227588c
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 16 deletions.
2 changes: 1 addition & 1 deletion fuzzing/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ toolset.flags $(__name__).make-corpus PYTHON <python.interpreter> ;

for local fuzzer in $(all_fuzzers)
{
local fuzz_time = 60 ;
local fuzz_time = 30 ;
local corpus = /tmp/corpus/$(fuzzer) ;
local min_corpus = /tmp/mincorpus/$(fuzzer) ;
local seed_corpus = $(HERE)/seedcorpus/$(fuzzer) ;
Expand Down
35 changes: 21 additions & 14 deletions include/boost/decimal/charconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,16 +542,16 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const
{
append_trailing_zeros = true;
}
}

// In general formatting we remove trailing 0s
if (fmt == chars_format::general)
{

const auto zeros_removal {remove_trailing_zeros(significand)};
significand = zeros_removal.trimmed_number;
exponent += static_cast<int>(zeros_removal.number_of_removed_zeros);
num_dig -= static_cast<int>(zeros_removal.number_of_removed_zeros);
}
// In general formatting we remove trailing 0s
// Same with unspecified precision fixed formatting
if ((precision == -1 && fmt == chars_format::fixed) || fmt == chars_format::general)
{
const auto zeros_removal {remove_trailing_zeros(significand)};
significand = zeros_removal.trimmed_number;
exponent += static_cast<int>(zeros_removal.number_of_removed_zeros);
num_dig -= static_cast<int>(zeros_removal.number_of_removed_zeros);
}

// Make sure the result will fit in the buffer
Expand All @@ -569,25 +569,32 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const
*first++ = '0';
return {first, std::errc()};
}
else if (num_leading_zeros > precision)
else if (precision != -1 && num_leading_zeros > precision)
{
*first++ = '0';
*first++ = '.';
std::memset(first, '0', static_cast<std::size_t>(precision));
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(precision));
return {first + precision, std::errc()};
}
else
{
*first++ = '0';
*first++ = '.';
std::memset(first, '0', static_cast<std::size_t>(num_leading_zeros));
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(num_leading_zeros));
first += num_leading_zeros;

// We can skip the rest if there's nothing more to do for the required precision
if (significand == 0)
{
std::memset(first, '0', static_cast<std::size_t>(precision - num_leading_zeros));
return {first + precision, std::errc()};
if (precision - num_leading_zeros > 0)
{
boost::decimal::detail::memset(first, '0', static_cast<std::size_t>(precision - num_leading_zeros));
return {first + precision, std::errc()};
}
else
{
return {first, std::errc()};
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions include/boost/decimal/detail/memcpy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#if defined(__GNUC__) && __GNUC__ >= 10
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wstringop-overflow"
# pragma GCC diagnostic ignored "-Warray-bounds"
# define BOOST_DECIMAL_STRINGOP_OVERFLOW_DISABLED
#endif

Expand Down
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ project : requirements
run-fail benchmarks.cpp ;
run compare_dec128_and_fast.cpp ;
compile-fail concepts_test.cpp ;
run crash_report_1.cpp ;
run github_issue_426.cpp ;
run github_issue_448.cpp ;
run-fail github_issue_519.cpp ;
Expand Down
27 changes: 27 additions & 0 deletions test/crash_report_1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/decimal.hpp>
#include <boost/core/lightweight_test.hpp>
#include <iostream>

using namespace boost::decimal;

int main()
{
char buffer[64]{};
const auto print_val {0.000001_df};

const auto r = boost::decimal::to_chars(
buffer,
buffer + sizeof(buffer),
print_val,
boost::decimal::chars_format::fixed
);
*r.ptr = '\0';

BOOST_TEST_CSTR_EQ(buffer, "0.000001");

return boost::report_errors();
}
35 changes: 34 additions & 1 deletion test/test_to_chars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static constexpr auto N = static_cast<std::size_t>(1024U >> 4U); // Number of tr
#if !defined(BOOST_DECIMAL_DISABLE_CLIB) && !(defined(__GNUC__) && __GNUC__ >= 13 && !defined(__aarch64__))

template <typename T>
void test_value(T val, const char* result, chars_format fmt, int precision = -1)
void test_value(T val, const char* result, chars_format fmt, int precision)
{
char buffer[256] {};
auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt, precision);
Expand All @@ -32,6 +32,16 @@ void test_value(T val, const char* result, chars_format fmt, int precision = -1)
BOOST_TEST_CSTR_EQ(result, buffer);
}

template <typename T>
void test_value(T val, const char* result, chars_format fmt)
{
char buffer[256] {};
auto r = to_chars(buffer, buffer + sizeof(buffer), val, fmt);
*r.ptr = '\0';
BOOST_TEST(r);
BOOST_TEST_CSTR_EQ(result, buffer);
}

template <typename T>
void test_value(T val, const char* result)
{
Expand Down Expand Up @@ -764,6 +774,25 @@ void test_777()
test_value(value3, "-2111000000", chars_format::fixed, 0);
}

template <typename T>
void test_more_powers_10()
{
test_value(T{1, -6}, "0.000001", chars_format::fixed);
test_value(T{1, -5}, "0.00001", chars_format::fixed);
test_value(T{1, -4}, "0.0001", chars_format::fixed);
test_value(T{1, -3}, "0.001", chars_format::fixed);
test_value(T{1, -2}, "0.01", chars_format::fixed);
test_value(T{1, -1}, "0.1", chars_format::fixed);
test_value(T{1, 0}, "1", chars_format::fixed);
test_value(T{1, 1}, "10", chars_format::fixed);
test_value(T{1, 2}, "100", chars_format::fixed);
test_value(T{1, 3}, "1000", chars_format::fixed);
test_value(T{1, 4}, "10000", chars_format::fixed);
test_value(T{1, 5}, "100000", chars_format::fixed);
test_value(T{1, 6}, "1000000", chars_format::fixed);
test_value(T{1, 7}, "10000000", chars_format::fixed);
}

int main()
{
test_non_finite_values<decimal32>();
Expand Down Expand Up @@ -874,6 +903,10 @@ int main()
test_777<decimal64_fast>();
test_777<decimal128_fast>();

test_more_powers_10<decimal32>();
test_more_powers_10<decimal64>();
test_more_powers_10<decimal128>();

return boost::report_errors();
}

Expand Down

0 comments on commit 227588c

Please sign in to comment.