2626#include < boost/decimal/detail/countl.hpp>
2727#include < boost/decimal/detail/remove_trailing_zeros.hpp>
2828#include < boost/decimal/detail/promotion.hpp>
29+ #include < boost/decimal/detail/quantum_preservation_format.hpp>
2930
3031#ifndef BOOST_DECIMAL_BUILD_MODULE
3132#include < cstdint>
@@ -1109,6 +1110,67 @@ constexpr auto to_chars_hex_impl(char* first, char* last, const TargetDecimalTyp
11091110 return to_chars_integer_impl<std::uint32_t >(first, last, static_cast <std::uint32_t >(abs_exp));
11101111}
11111112
1113+ template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
1114+ constexpr auto to_chars_cohort_preserving_scientific (char * first, char * last, const TargetDecimalType& value) noexcept -> to_chars_result
1115+ {
1116+ using unsigned_integer = typename TargetDecimalType::significand_type;
1117+
1118+ const auto fp = fpclassify (value);
1119+ if (!(fp == FP_NORMAL || fp == FP_SUBNORMAL))
1120+ {
1121+ // Cohorts are irrelevant for non-finite values
1122+ return to_chars_nonfinite (first, last, value, fp, chars_format::scientific, -1 );
1123+ }
1124+
1125+ // First we print the significand of the number by decoding the value,
1126+ // and using our existing to_chars for integers
1127+ //
1128+ // We possibly offset the to_chars by one in the event that we know we will have a fraction
1129+ const auto components {value.to_components ()};
1130+ const auto significand {components.sig };
1131+ auto exponent {static_cast <int >(components.exp )};
1132+
1133+ if (components.sign )
1134+ {
1135+ *first++ = ' -' ;
1136+ }
1137+
1138+ const bool fractional_piece {significand > 10U };
1139+ const auto r {to_chars_integer_impl<unsigned_integer>(first + static_cast <std::ptrdiff_t >(fractional_piece), last, significand)};
1140+
1141+ if (BOOST_DECIMAL_UNLIKELY (!r))
1142+ {
1143+ return r; // LCOV_EXCL_LINE
1144+ }
1145+
1146+ // If there is more than one digit in the significand then we are going to need to:
1147+ // First: insert a decimal point
1148+ // Second: figure out how many decimal points we are going to have to adjust the exponent accordingly
1149+ if (fractional_piece)
1150+ {
1151+ *first = *(first + 1 );
1152+ *(first + 1 ) = ' .' ;
1153+
1154+ const auto offset {num_digits (significand) - 1 };
1155+ exponent += offset;
1156+ }
1157+
1158+ // Insert the exponent characters ensuring that there are always at least two digits after the "e",
1159+ // e.g. e+07 not e+7
1160+ first = r.ptr ;
1161+ *first++ = ' e' ;
1162+ const bool negative_exp {exponent < 0 };
1163+ *first++ = negative_exp ? ' -' : ' +' ;
1164+
1165+ const auto abs_exp { static_cast <std::uint32_t >(negative_exp ? -exponent : exponent) };
1166+ if (abs_exp < 10U )
1167+ {
1168+ *first++ = ' 0' ;
1169+ }
1170+
1171+ return to_chars_integer_impl<std::uint32_t >(first, last, abs_exp);
1172+ }
1173+
11121174#ifdef _MSC_VER
11131175# pragma warning(push)
11141176# pragma warning(disable: 4702) // Unreachable code
@@ -1181,6 +1243,34 @@ constexpr auto to_chars_impl(char* first, char* last, const TargetDecimalType& v
11811243 return to_chars_scientific_impl (first, last, value, fmt, local_precision); // LCOV_EXCL_LINE
11821244}
11831245
1246+ // TODO(mborland): Remove once other modes are inplace
1247+ #ifdef _MSC_VER
1248+ # pragma warning(push)
1249+ # pragma warning(disable: 4065)
1250+ #endif
1251+
1252+ template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
1253+ constexpr auto to_chars_impl (char * first, char * last, const TargetDecimalType& value, const chars_format fmt, const quantum_preservation method) noexcept -> to_chars_result
1254+ {
1255+ // No quantum preservation is the same thing as regular to_chars
1256+ if (method == quantum_preservation::off)
1257+ {
1258+ return to_chars_impl (first, last, value, fmt);
1259+ }
1260+
1261+ // Sanity check our bounds
1262+ if (BOOST_DECIMAL_UNLIKELY (first >= last))
1263+ {
1264+ return {last, std::errc::invalid_argument};
1265+ }
1266+
1267+ switch (fmt)
1268+ {
1269+ default :
1270+ return to_chars_cohort_preserving_scientific (first, last, value);
1271+ }
1272+ }
1273+
11841274#ifdef _MSC_VER
11851275# pragma warning(pop)
11861276#endif
@@ -1194,13 +1284,13 @@ constexpr auto to_chars(char* first, char* last, const TargetDecimalType& value)
11941284}
11951285
11961286BOOST_DECIMAL_EXPORT template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
1197- constexpr auto to_chars (char * first, char * last, const TargetDecimalType& value, chars_format fmt) noexcept -> to_chars_result
1287+ constexpr auto to_chars (char * first, char * last, const TargetDecimalType& value, const chars_format fmt) noexcept -> to_chars_result
11981288{
11991289 return detail::to_chars_impl (first, last, value, fmt);
12001290}
12011291
12021292BOOST_DECIMAL_EXPORT template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
1203- constexpr auto to_chars (char * first, char * last, const TargetDecimalType& value, chars_format fmt, int precision) noexcept -> to_chars_result
1293+ constexpr auto to_chars (char * first, char * last, const TargetDecimalType& value, const chars_format fmt, int precision) noexcept -> to_chars_result
12041294{
12051295 if (precision < 0 )
12061296 {
@@ -1210,6 +1300,13 @@ constexpr auto to_chars(char* first, char* last, const TargetDecimalType& value,
12101300 return detail::to_chars_impl (first, last, value, fmt, precision);
12111301}
12121302
1303+ BOOST_DECIMAL_EXPORT template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
1304+ constexpr auto to_chars (char * first, char * last, const TargetDecimalType& value, const chars_format fmt, const quantum_preservation method) noexcept -> to_chars_result
1305+ {
1306+ static_assert (detail::is_ieee_type_v<TargetDecimalType>, " Fast types are automatically normalized, so they have no concept of quantum preservation" );
1307+ return detail::to_chars_impl (first, last, value, fmt, method);
1308+ }
1309+
12131310#ifdef BOOST_DECIMAL_HAS_STD_CHARCONV
12141311
12151312BOOST_DECIMAL_EXPORT template <typename DecimalType>
0 commit comments