7 #if !defined(CNL_IMPL_SCALED_INTEGER_TO_CHARS_H)
8 #define CNL_IMPL_SCALED_INTEGER_TO_CHARS_H
10 #include "../../integer.h"
11 #include "../../rounding_integer.h"
12 #include "../charconv/constants.h"
13 #include "../charconv/descale.h"
14 #include "../cnl_assert.h"
15 #include "../cstdint/types.h"
16 #include "../num_traits/fixed_width_scale.h"
17 #include "../num_traits/to_rep.h"
18 #include "../scaled/power.h"
20 #include "../ssizeof.h"
21 #include "definition.h"
31 #include <system_error>
33 #include <type_traits>
39 constexpr
auto num_digits_from_binary(
int num_digits,
int radix)
45 return (num_digits + 2) / 3;
47 return (num_digits * 1000 + 3322) / 3321;
49 return (num_digits + 3) / 4;
51 auto const binary_digits_per_digit{used_digits(radix - 1)};
52 return (num_digits + binary_digits_per_digit - 1) / binary_digits_per_digit;
57 constexpr
auto num_digits_to_binary(
int num_digits,
int radix)
63 return num_digits * 3;
65 return (num_digits * 3322 + 678) / 1000;
67 return num_digits * 4;
69 return num_digits * used_digits(radix - 1);
73 template<
typename Rep,
int Exponent,
int Radix>
74 struct max_to_chars_chars<
scaled_integer<Rep, power<Exponent, Radix>>> {
79 static constexpr
auto _fractional_digits =
80 std::max(cnl::_impl::fractional_digits<scalar>, 0);
82 static constexpr
auto _sign_chars =
static_cast<int>(cnl::numbers::signedness_v<scalar>);
83 static constexpr
auto _num_significant_integer_bits{cnl::digits_v<scalar> - _fractional_digits};
84 static constexpr
auto _num_trailing_integer_bits{
85 num_digits_to_binary(
std::max(0, Exponent), Radix)};
86 static constexpr
auto _num_integer_bits{
87 _num_significant_integer_bits + _num_trailing_integer_bits};
88 static constexpr
auto _integer_chars = num_digits_from_binary(_num_integer_bits, 10);
89 static constexpr
auto _radix_chars =
static_cast<int>(_fractional_digits > 0);
90 static constexpr
auto _fractional_chars =
std::max(0, _fractional_digits);
93 static constexpr
auto value =
94 _sign_chars + _integer_chars + _radix_chars + _fractional_chars;
97 struct descaled_info {
98 std::string_view significand_digits;
99 std::string_view exponent_chars;
100 std::span<char> output;
101 int num_significand_digits{};
104 bool exponent_has_sign{};
107 struct fixed_solution {
108 int num_significand_digits;
115 [[nodiscard]] constexpr
auto solve_fixed(descaled_info
const& info)
117 auto const num_integer_digits{info.num_significand_digits + info.exponent};
118 if (num_integer_digits > info.max_chars) {
119 return fixed_solution{};
122 auto const leading_zeros{
std::max(0, -num_integer_digits)};
123 auto const has_radix{info.exponent < 0};
124 auto const trailing_zeros{
std::max(0, info.exponent)};
125 auto const unbounded_num_chars{info.num_significand_digits + leading_zeros + has_radix + trailing_zeros};
126 auto const chars_truncated{
std::max(0, unbounded_num_chars - info.max_chars)};
127 auto const truncated_significand_digits{info.num_significand_digits - chars_truncated};
128 auto const truncated_num_chars{unbounded_num_chars - chars_truncated};
130 return fixed_solution{
131 truncated_significand_digits,
138 [[nodiscard]] constexpr
auto fill(
139 descaled_info
const& info,
140 fixed_solution
const& solution)
144 auto significand_digits_first =
std::begin(info.significand_digits);
147 for (
auto n{
std::max(0, info.num_significand_digits +
std::min(0, info.exponent))};
150 CNL_ASSERT(*significand_digits_first);
151 *out++ = *significand_digits_first++;
154 if (solution.trailing_zeros) {
155 out =
std::fill_n(out, solution.trailing_zeros, zero_char);
156 }
else if (out <
std::end(info.output)) {
157 if (solution.has_radix) {
161 out =
std::fill_n(out, solution.leading_zeros, zero_char);
165 significand_digits_first,
170 CNL_ASSERT(significand_digits_first <=
std::end(info.significand_digits));
171 CNL_ASSERT(out ==
std::begin(info.output) + solution.num_chars);
172 CNL_ASSERT(out <=
std::end(info.output));
173 return std::to_chars_result{&*out,
std::errc{}};
176 struct scientific_solution {
177 int num_significand_digits;
181 [[nodiscard]] constexpr
auto solve_scientific(descaled_info
const& info)
183 auto const num_exponent_chars{_impl::ssize(info.exponent_chars)};
185 auto const unbounded_num_chars{
186 info.num_significand_digits + _impl::ssizeof(radix_char) + _impl::ssizeof(e_char) + num_exponent_chars};
187 auto const chars_truncated{
std::max(0, unbounded_num_chars - info.max_chars)};
189 return scientific_solution{
190 info.num_significand_digits - chars_truncated,
191 unbounded_num_chars - chars_truncated};
194 [[nodiscard]] constexpr
auto fill(
195 descaled_info
const& info,
196 scientific_solution
const& solution)
200 auto significand_digits_first =
std::begin(info.significand_digits);
203 while (!
isdigit(*out++ = *significand_digits_first++)) {
204 CNL_ASSERT(out <
std::end(info.output));
212 significand_digits_first,
225 CNL_ASSERT(out ==
std::begin(info.output) + solution.num_chars);
226 CNL_ASSERT(out <=
std::end(info.output));
227 return std::to_chars_result{&*out,
std::errc{}};
230 [[nodiscard]]
inline constexpr
auto to_chars_positive(
233 std::string_view
const& significand_digits,
236 _impl::descaled_info info;
237 info.significand_digits = significand_digits;
238 info.output = std::span<char>{first, last};
239 info.num_significand_digits = _impl::ssize(info.significand_digits);
240 info.exponent = exponent;
241 info.max_chars = _impl::ssize(info.output);
243 auto const exponent_chars_static{to_chars_static(exponent + info.num_significand_digits - 1)};
244 info.exponent_chars = std::string_view(
245 exponent_chars_static.chars.data(),
246 exponent_chars_static.length);
247 info.exponent_has_sign = info.exponent_chars[0] == _impl::minus_char;
249 CNL_ASSERT(
isdigit(info.exponent_chars[0]) || info.exponent_chars[0] == _impl::minus_char);
251 auto const scientific_solution{_impl::solve_scientific(info)};
252 auto const fixed_solution{_impl::solve_fixed(info)};
255 scientific_solution.num_significand_digits,
256 -scientific_solution.num_chars}
258 fixed_solution.num_significand_digits,
259 -fixed_solution.num_chars}) {
260 CNL_ASSERT(scientific_solution.num_significand_digits > 0);
261 return _impl::fill(info, scientific_solution);
264 if (fixed_solution.num_significand_digits > 0) {
265 return _impl::fill(info, fixed_solution);
268 return std::to_chars_result{last, std::errc::value_too_large};
271 template<
integer Significand,
int Radix>
272 [[nodiscard]] constexpr
auto to_chars_non_zero(
275 descaled<Significand, Radix>
const& descaled)
277 CNL_ASSERT(descaled.significand);
279 auto const significand_chars_static{to_chars_static(descaled.significand)};
280 auto const significand_chars_cstr{significand_chars_static.chars.data()};
281 if (*significand_chars_cstr == minus_char) {
283 return to_chars_positive(first + 1, last, std::string_view(significand_chars_cstr + 1, significand_chars_static.length - 1), descaled.exponent);
286 return to_chars_positive(first, last, std::string_view(significand_chars_cstr, significand_chars_static.length), descaled.exponent);
292 template<
integer Rep,
int Exponent,
int Radix>
293 [[nodiscard]]
inline constexpr
auto to_chars(
300 return std::to_chars_result{last, std::errc::value_too_large};
305 *first = _impl::zero_char;
306 return std::to_chars_result{first + 1,
std::errc{}};
309 using significand_type = std::conditional_t<(digits_v<Rep> > digits_v<std::int64_t>), Rep,
std::int64_t>;
310 auto const descaled{_impl::descale<significand_type, 10>(
311 _impl::to_rep(value), power<Exponent, Radix>{})};
313 return _impl::to_chars_non_zero(first, last, descaled);
317 #endif // CNL_IMPL_SCALED_INTEGER_TO_CHARS_H