460 using RoundPolicy = TRoundPolicy;
469 template <
typename Int, impl::EnableIfInt<Int> = 0>
488 explicit constexpr Decimal(std::string_view value);
497 template <
typename T>
499 static_assert(std::is_floating_point_v<T>);
502 static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
503 static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
505 const auto unbiased_float =
static_cast<
long double>(value) *
kDecimalFactor;
506 if (unbiased_float < impl::kMinRepresentableLongDouble || unbiased_float > impl::kMaxRepresentableLongDouble) {
532 result.value_ = value;
548 const int exponent_for_pack = Prec - original_precision;
550 if (exponent_for_pack >= 0) {
563 *
this = FromDecimal(rhs);
568 template <
typename Int, impl::EnableIfInt<Int> = 0>
574#ifdef USERVER_IMPL_HAS_THREE_WAY_COMPARISON
575 constexpr auto operator<=>(
const Decimal& rhs)
const =
default;
590 constexpr Decimal operator+()
const {
return *
this; }
592 constexpr Decimal operator-()
const {
593 if (value_ == impl::kMinInt64) {
600 constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs)
const {
601 if constexpr (Prec2 > Prec) {
602 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
603 }
else if constexpr (Prec2 < Prec) {
604 return *
this + FromDecimal(rhs);
607 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
614 template <
typename Int, impl::EnableIfInt<Int> = 0>
615 constexpr Decimal operator+(Int rhs)
const {
619 template <
typename Int, impl::EnableIfInt<Int> = 0>
626 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
631 template <
typename Int, impl::EnableIfInt<Int> = 0>
632 constexpr Decimal& operator+=(Int rhs) {
638 constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs)
const {
639 if constexpr (Prec2 > Prec) {
640 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
641 }
else if constexpr (Prec2 < Prec) {
642 return *
this - FromDecimal(rhs);
645 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
652 template <
typename Int, impl::EnableIfInt<Int> = 0>
653 constexpr Decimal operator-(Int rhs)
const {
657 template <
typename Int, impl::EnableIfInt<Int> = 0>
664 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
669 template <
typename Int, impl::EnableIfInt<Int> = 0>
670 constexpr Decimal& operator-=(Int rhs) {
675 template <
typename Int,
typename = impl::EnableIfInt<Int>>
676 constexpr Decimal operator*(Int rhs)
const {
678 if (rhs > impl::kMaxInt64 || __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
684 template <
typename Int, impl::EnableIfInt<Int> = 0>
689 template <
typename Int, impl::EnableIfInt<Int> = 0>
690 constexpr Decimal& operator*=(Int rhs) {
696 constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs)
const {
706 template <
typename Int,
typename = impl::EnableIfInt<Int>>
707 constexpr Decimal operator/(Int rhs)
const {
711 template <
typename Int,
typename = impl::EnableIfInt<Int>>
716 template <
typename Int,
typename = impl::EnableIfInt<Int>>
717 constexpr Decimal& operator/=(Int rhs) {
723 constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs)
const {
734 constexpr int Sign()
const {
return impl::Sign(value_); }
759 constexpr std::int64_t kLossLimit = (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
761 if (value_ > -kLossLimit && value_ < kLossLimit) {
765 constexpr int kCoef =
767 << (std::max(std::numeric_limits<std::int64_t>::digits - std::numeric_limits<
double>::digits - 3 * Prec, 0)
771 const std::int64_t p1 = value_ / (
kDecimalFactor * kCoef) * kCoef;
788 template <
int Prec2,
typename RoundPolicy2>
789 static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
790 if constexpr (Prec > Prec2) {
792 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>, &result)) {
796 }
else if constexpr (Prec < Prec2) {
797 return FromUnbiased(impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
803 template <
int Prec2,
typename RoundPolicy2>
806 template <
typename T,
int OldPrec,
typename OldRound>
815struct IsDecimal : std::false_type {};
817template <
int Prec,
typename RoundPolicy>
818struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
839template <
typename T,
int OldPrec,
typename OldRound>
841 static_assert(kIsDecimal<T>);
842 return T::FromDecimal(arg);
850template <
int Prec,
typename RoundPolicy>
851constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after) {
852 using Dec =
Decimal<Prec, RoundPolicy>;
853 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
856 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) || __builtin_add_overflow(result, after, &result))
861 return Dec::FromUnbiased(result);
865template <
int Prec,
typename RoundPolicy>
866constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
int original_precision) {
867 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
870 if (original_precision <= Prec) {
872 const int missing_digits = Prec - original_precision;
873 const int64_t factor =
Pow10(missing_digits
);
874 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
877 const int extra_digits = original_precision - Prec;
878 const int64_t factor =
Pow10(extra_digits
);
881 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
882 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
886template <
int Prec,
typename RoundPolicy>
887constexpr Decimal<Prec, RoundPolicy> FromUnpackedWithExponent(
890 int original_precision,
892 bool is_negative_exponent,
895 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
898 if (before == 0 && after == 0) {
899 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
902 const int effective_exponent = is_negative_exponent ? -exponent : exponent;
904 if (effective_exponent + Prec < -kMaxDecimalDigits) {
905 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
908 int total_scale = effective_exponent + Prec - original_precision;
910 if (before == 0 && after != 0) {
911 if (!is_negative_exponent) {
912 if (exponent >= leading_zeros) {
913 exponent -= leading_zeros;
914 original_precision -= leading_zeros;
916 original_precision -= exponent;
922 int64_t value = before;
924 if (__builtin_mul_overflow(value,
Pow10(original_precision
), &value) ||
925 __builtin_add_overflow(value, after, &value))
930 if (total_scale > 0) {
931 if (total_scale > kMaxDecimalDigits) {
934 if (__builtin_mul_overflow(value,
Pow10(total_scale
), &value)) {
937 }
else if (total_scale < 0) {
938 const int64_t divisor =
Pow10(-total_scale
);
939 value = Div<RoundPolicy>(value, divisor);
942 return Decimal<Prec, RoundPolicy>::FromUnbiased(value);
945struct UnpackedDecimal {
953template <
int Prec,
typename RoundPolicy>
954constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
955 using Dec =
Decimal<Prec, RoundPolicy>;
956 return {dec.AsUnbiased() / Dec::kDecimalFactor, dec.AsUnbiased() % Dec::kDecimalFactor};
962template <
int Prec,
typename RoundPolicy>
963UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
964 if (new_prec == Prec) {
965 return AsUnpacked(dec);
968 if (new_prec > Prec) {
969 if (__builtin_mul_overflow(dec.AsUnbiased(),
Pow10(new_prec - Prec
), &result)) {
973 result = impl::Div<RoundPolicy>(dec.AsUnbiased(),
Pow10(Prec - new_prec
));
975 const auto dec_factor =
Pow10(new_prec
);
976 return {result / dec_factor, result % dec_factor};
979template <
typename CharT>
980constexpr bool IsSpace(CharT c) {
981 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
984template <
typename CharT,
typename Traits>
985class StringCharSequence {
987 explicit constexpr StringCharSequence(std::basic_string_view<CharT, Traits> sv)
988 : current_(sv.begin()),
993 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
995 constexpr void Unget() { --current_; }
998 typename std::basic_string_view<CharT, Traits>::iterator current_;
999 typename std::basic_string_view<CharT, Traits>::iterator end_;
1002template <
typename CharT,
typename Traits>
1003class StreamCharSequence {
1005 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in)
1011 constexpr CharT kEof = std::basic_istream<CharT, Traits>::traits_type::eof();
1015 const CharT c = in_->peek();
1023 void Unget() { in_->unget(); }
1026 std::basic_istream<CharT, Traits>* in_;
1029enum class ParseOptions {
1034 kAllowSpaces = 1 << 0,
1038 kAllowTrailingJunk = 1 << 1,
1042 kAllowBoundaryDot = 1 << 2,
1046 kAllowRounding = 1 << 3,
1050 kAllowExponent = 1 << 4
1053enum class ParseErrorCode : uint8_t {
1077 kExponentNotAllowed,
1084struct ParseUnpackedResult {
1087 uint8_t decimal_digits{0};
1088 bool is_negative{
false};
1089 uint8_t exponent{0};
1090 bool is_negative_exponent{
false};
1091 std::optional<ParseErrorCode> error;
1092 uint32_t error_position{-1U};
1093 int zeros_after_dec{0};
1096enum class ParseState {
1131constexpr inline void StateToExpSign(
1132 std::optional<ParseErrorCode>& error,
1134 utils::Flags<ParseOptions>& options
1136 if (!error && !(options & ParseOptions::kAllowExponent)) {
1137 error = ParseErrorCode::kExponentNotAllowed;
1139 state = ParseState::kExpSign;
1143template <
typename CharSequence>
1144[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(CharSequence input, utils::Flags<ParseOptions> options) {
1145 constexpr char dec_point =
'.';
1149 bool is_negative =
false;
1151 ptrdiff_t position = -1;
1152 auto state = ParseState::kSign;
1153 std::optional<ParseErrorCode> error;
1154 int before_digit_count = 0;
1155 uint8_t after_digit_count = 0;
1156 bool is_negative_exp =
false;
1157 uint8_t exponent = 0;
1158 int zeros_after_dec = 0;
1160 while (state != ParseState::kEnd) {
1161 const auto c = input.Get();
1170 case ParseState::kSign:
1173 state = ParseState::kBeforeFirstDig;
1174 }
else if (c ==
'+') {
1175 state = ParseState::kBeforeFirstDig;
1176 }
else if (c ==
'0') {
1177 state = ParseState::kLeadingZeros;
1178 before_digit_count = 1;
1179 }
else if ((c >=
'1') && (c <=
'9')) {
1180 state = ParseState::kBeforeDec;
1181 before =
static_cast<
int>(c -
'0');
1182 before_digit_count = 1;
1183 }
else if (c == dec_point) {
1184 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1185 error = ParseErrorCode::kBoundaryDot;
1187 state = ParseState::kZerosAfterDec;
1188 }
else if (IsSpace(c)) {
1189 if (!(options & ParseOptions::kAllowSpaces)) {
1190 state = ParseState::kEnd;
1191 error = ParseErrorCode::kSpace;
1194 state = ParseState::kEnd;
1195 error = ParseErrorCode::kWrongChar;
1198 case ParseState::kBeforeFirstDig:
1200 state = ParseState::kLeadingZeros;
1201 before_digit_count = 1;
1202 }
else if ((c >=
'1') && (c <=
'9')) {
1203 state = ParseState::kBeforeDec;
1204 before =
static_cast<
int>(c -
'0');
1205 before_digit_count = 1;
1206 }
else if (c == dec_point) {
1207 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1208 error = ParseErrorCode::kBoundaryDot;
1210 state = ParseState::kAfterDec;
1212 state = ParseState::kEnd;
1213 error = ParseErrorCode::kWrongChar;
1216 case ParseState::kLeadingZeros:
1219 }
else if ((c >=
'1') && (c <=
'9')) {
1220 state = ParseState::kBeforeDec;
1221 before =
static_cast<
int>(c -
'0');
1222 }
else if (c == dec_point) {
1223 state = ParseState::kZerosAfterDec;
1224 }
else if (c ==
'e' || c ==
'E') {
1225 StateToExpSign( error, state, options);
1227 state = ParseState::kEnd;
1230 case ParseState::kBeforeDec:
1231 if ((c >=
'0') && (c <=
'9')) {
1232 if (before_digit_count < kMaxDecimalDigits) {
1233 before = 10 * before +
static_cast<
int>(c -
'0');
1234 before_digit_count++;
1235 }
else if (!error) {
1236 error = ParseErrorCode::kOverflow;
1238 }
else if (c == dec_point) {
1239 state = ParseState::kZerosAfterDec;
1240 }
else if (c ==
'e' || c ==
'E') {
1241 StateToExpSign( error, state, options);
1243 state = ParseState::kEnd;
1247 case ParseState::kZerosAfterDec:
1250 after_digit_count++;
1255 case ParseState::kAfterDec:
1256 state = ParseState::kAfterDec;
1257 if ((c >=
'0') && (c <=
'9')) {
1258 if (after_digit_count < kMaxDecimalDigits) {
1259 after = 10 * after +
static_cast<
int>(c -
'0');
1260 after_digit_count++;
1262 if (!(options & ParseOptions::kAllowRounding) && !error) {
1263 error = ParseErrorCode::kRounding;
1265 state = ParseState::kIgnoringAfterDec;
1272 }
else if (c ==
'e' || c ==
'E') {
1273 StateToExpSign( error, state, options);
1275 if (!(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0 && !error) {
1276 error = ParseErrorCode::kBoundaryDot;
1278 state = ParseState::kEnd;
1282 case ParseState::kIgnoringAfterDec:
1283 if ((c >=
'0') && (c <=
'9')) {
1285 }
else if (c ==
'e' || c ==
'E') {
1286 StateToExpSign( error, state, options);
1288 state = ParseState::kEnd;
1292 case ParseState::kExpSign:
1294 is_negative_exp =
false;
1295 state = ParseState::kExpFirstDigit;
1296 }
else if (c ==
'-') {
1297 is_negative_exp =
true;
1298 state = ParseState::kExpFirstDigit;
1299 }
else if (c >=
'0' && c <=
'9') {
1300 exponent =
static_cast<
int>(c -
'0');
1301 state = ParseState::kExpDigits;
1304 error = ParseErrorCode::kWrongChar;
1306 state = ParseState::kEnd;
1310 case ParseState::kExpFirstDigit:
1311 if (c >=
'0' && c <=
'9') {
1312 exponent =
static_cast<
int>(c -
'0');
1313 state = ParseState::kExpDigits;
1315 state = ParseState::kEnd;
1317 error = ParseErrorCode::kWrongChar;
1322 case ParseState::kExpDigits:
1323 if (c >=
'0' && c <=
'9') {
1324 if ((__builtin_mul_overflow(10, exponent, &exponent) ||
1325 __builtin_add_overflow(
static_cast<
int>(c -
'0'), exponent, &exponent)))
1327 if (is_negative_exp && !(options & ParseOptions::kAllowRounding) && !error) {
1328 error = ParseErrorCode::kRounding;
1330 if (!is_negative_exp && !error) {
1331 error = ParseErrorCode::kOverflow;
1335 state = ParseState::kEnd;
1339 case ParseState::kEnd:
1345 if (state == ParseState::kEnd) {
1348 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1349 if (!(options & ParseOptions::kAllowSpaces)) {
1350 error = ParseErrorCode::kSpace;
1355 const auto c = input.Get();
1361 error = ParseErrorCode::kTrailingJunk;
1369 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1370 error = ParseErrorCode::kNoDigits;
1373 if (!error && state == ParseState::kZerosAfterDec && !(options & ParseOptions::kAllowBoundaryDot) &&
1374 after_digit_count == 0)
1376 error = ParseErrorCode::kBoundaryDot;
1379 if ((!error && state == ParseState::kExpSign) || (!error && state == ParseState::kExpFirstDigit)) {
1380 error = ParseErrorCode::kNoExponentDigits;
1383 if (zeros_after_dec >= kMaxDecimalDigits) {
1384 after_digit_count = 0;
1386 zeros_after_dec = 0;
1397 static_cast<uint32_t>(position),
1403template <
int Prec,
typename RoundPolicy>
1405 Decimal<Prec, RoundPolicy> decimal;
1406 std::optional<ParseErrorCode> error;
1407 uint32_t error_position{-1U};
1411template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1412[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(CharSequence input, utils::Flags<ParseOptions> options) {
1413 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1416 return {{}, parsed.error, parsed.error_position};
1419 if (!(options & ParseOptions::kAllowRounding) && parsed.decimal_digits > Prec) {
1420 return {{}, ParseErrorCode::kRounding, 0};
1423 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1424 return {{}, ParseErrorCode::kOverflow, 0};
1427 if (parsed.after == 0 && parsed.decimal_digits > 0) {
1429 parsed.decimal_digits = 0;
1430 parsed.zeros_after_dec = 0;
1433 if (parsed.is_negative) {
1434 parsed.before = -parsed.before;
1435 parsed.after = -parsed.after;
1438 if (parsed.exponent != 0) {
1440 FromUnpackedWithExponent<Prec, RoundPolicy>(
1443 parsed.decimal_digits,
1445 parsed.is_negative_exponent,
1446 parsed.zeros_after_dec
1453 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after, parsed.decimal_digits), {}, 0};
1456std::string GetErrorMessage(std::string_view source, std::string_view path, size_t position, ParseErrorCode reason);
1460void TrimTrailingZeros(int64_t& after,
int& after_precision);
1462std::string ToString(int64_t before, int64_t after,
int precision,
const FormatOptions& format_options);
1466template <
int Prec,
typename RoundPolicy>
1468 const auto result = impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(value), impl::ParseOptions::kNone);
1471 throw ParseError(impl::GetErrorMessage(value,
"<string>", result.error_position, *result.error));
1473 *
this = result.decimal;
1476template <
int Prec,
typename RoundPolicy>
1478 const auto result = impl::Parse<Prec, RoundPolicy>(
1479 impl::StringCharSequence(input),
1480 {impl::ParseOptions::kAllowSpaces,
1481 impl::ParseOptions::kAllowBoundaryDot,
1482 impl::ParseOptions::kAllowRounding,
1483 impl::ParseOptions::kAllowExponent}
1487 throw ParseError(impl::GetErrorMessage(input,
"<string>", result.error_position, *result.error));
1489 return result.decimal;
1500template <
int Prec,
typename RoundPolicy>
1502 return fmt::to_string(dec);
1516template <
int Prec,
typename RoundPolicy>
1518 auto precision = format_options
.precision.value_or(Prec);
1520 precision = std::min(precision, Prec);
1522 auto [before, after] = impl::AsUnpacked(dec, precision);
1523 return impl::ToString(before, after, precision, format_options);
1534template <
int Prec,
typename RoundPolicy>
1536 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1547template <
int NewPrec,
int Prec,
typename RoundPolicy>
1549 return ToStringTrailingZeros(
decimal64::decimal_cast<
Decimal<NewPrec, RoundPolicy>>(dec));
1567template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1568std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1569 if (is.flags() & std::ios_base::skipws) {
1572 const auto result = impl::Parse<Prec, RoundPolicy>(
1573 impl::StreamCharSequence(is),
1574 {impl::ParseOptions::kAllowTrailingJunk, impl::ParseOptions::kAllowExponent}
1578 is.setstate(std::ios_base::failbit);
1587template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1588std::basic_ostream<CharT, Traits>& operator<<(
1589 std::basic_ostream<CharT, Traits>& os,
1590 const Decimal<Prec, RoundPolicy>& d
1598template <
int Prec,
typename RoundPolicy>
1606template <
int Prec,
typename RoundPolicy,
typename Value>
1609 const std::string input = value.
template As<std::string>();
1612 impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(std::string_view{input}), impl::ParseOptions::kNone);
1615 throw ParseError(impl::GetErrorMessage(input, value.GetPath(), result.error_position, *result.error));
1617 return result.decimal;
1622template <
int Prec,
typename RoundPolicy,
typename TargetType>
1624 return typename TargetType::Builder(ToString(object)).ExtractValue();
1629template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1631 WriteToStream(ToString(object), sw);
1635template <
int Prec,
typename RoundPolicy>
1642USERVER_NAMESPACE_END
1645template <
int Prec,
typename RoundPolicy>
1649 std::size_t operator()(
const Decimal& v)
const noexcept {
return std::hash<int64_t>{}(v.AsUnbiased()); }
1660template <
int Prec,
typename RoundPolicy,
typename Char>
1663 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1664 const auto* it = ctx.begin();
1665 const auto* end = ctx.end();
1667 if (it != end && *it ==
'.') {
1668 remove_trailing_zeros_ =
false;
1669 custom_precision_ = 0;
1671 while (it != end && *it >=
'0' && *it <=
'9') {
1672 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1677 if (!custom_precision_ && it != end && *it ==
'f') {
1678 remove_trailing_zeros_ =
false;
1682 if (it != end && *it !=
'}') {
1683 throw format_error(
"invalid format");
1689 template <
typename FormatContext>
1690 auto format(
const USERVER_NAMESPACE::
decimal64::
Decimal<Prec, RoundPolicy>& dec, FormatContext& ctx)
const {
1691 int after_digits = custom_precision_.value_or(Prec);
1692 auto [before, after] = USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1693 if (remove_trailing_zeros_) {
1694 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after, after_digits);
1697 if (after_digits > 0) {
1698 if (dec.Sign() == -1) {
1699 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before, -after, after_digits);
1701 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before, after, after_digits);
1704 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1709 bool remove_trailing_zeros_ =
true;
1710 std::optional<
int> custom_precision_;