443 using RoundPolicy = TRoundPolicy;
452 template <
typename Int, impl::EnableIfInt<Int> = 0>
469 explicit constexpr Decimal(std::string_view value);
478 template <
typename T>
480 static_assert(std::is_floating_point_v<T>);
483 static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
484 static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
486 const auto unbiased_float =
static_cast<
long double>(value) *
kDecimalFactor;
487 if (unbiased_float < impl::kMinRepresentableLongDouble || unbiased_float > impl::kMaxRepresentableLongDouble) {
513 result.value_ = value;
529 const int exponent_for_pack = Prec - original_precision;
531 if (exponent_for_pack >= 0) {
544 *
this = FromDecimal(rhs);
549 template <
typename Int, impl::EnableIfInt<Int> = 0>
555#ifdef USERVER_IMPL_HAS_THREE_WAY_COMPARISON
556 constexpr auto operator<=>(
const Decimal& rhs)
const =
default;
571 constexpr Decimal operator+()
const {
return *
this; }
573 constexpr Decimal operator-()
const {
579 constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs)
const {
580 if constexpr (Prec2 > Prec) {
581 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
582 }
else if constexpr (Prec2 < Prec) {
583 return *
this + FromDecimal(rhs);
586 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
593 template <
typename Int, impl::EnableIfInt<Int> = 0>
594 constexpr Decimal operator+(Int rhs)
const {
598 template <
typename Int, impl::EnableIfInt<Int> = 0>
605 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
610 template <
typename Int, impl::EnableIfInt<Int> = 0>
611 constexpr Decimal& operator+=(Int rhs) {
617 constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs)
const {
618 if constexpr (Prec2 > Prec) {
619 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
620 }
else if constexpr (Prec2 < Prec) {
621 return *
this - FromDecimal(rhs);
624 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
631 template <
typename Int, impl::EnableIfInt<Int> = 0>
632 constexpr Decimal operator-(Int rhs)
const {
636 template <
typename Int, impl::EnableIfInt<Int> = 0>
643 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
648 template <
typename Int, impl::EnableIfInt<Int> = 0>
649 constexpr Decimal& operator-=(Int rhs) {
654 template <
typename Int,
typename = impl::EnableIfInt<Int>>
655 constexpr Decimal operator*(Int rhs)
const {
657 if (rhs > impl::kMaxInt64 || __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
663 template <
typename Int, impl::EnableIfInt<Int> = 0>
668 template <
typename Int, impl::EnableIfInt<Int> = 0>
669 constexpr Decimal& operator*=(Int rhs) {
675 constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs)
const {
685 template <
typename Int,
typename = impl::EnableIfInt<Int>>
686 constexpr Decimal operator/(Int rhs)
const {
690 template <
typename Int,
typename = impl::EnableIfInt<Int>>
695 template <
typename Int,
typename = impl::EnableIfInt<Int>>
696 constexpr Decimal& operator/=(Int rhs) {
702 constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs)
const {
713 constexpr int Sign()
const {
return impl::Sign(value_); }
736 constexpr std::int64_t kLossLimit = (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
738 if (value_ > -kLossLimit && value_ < kLossLimit) {
742 constexpr int kCoef =
744 std::numeric_limits<std::int64_t>::digits - std::numeric_limits<
double>::digits - 3 * Prec, 0
748 const std::int64_t p1 = value_ / (
kDecimalFactor * kCoef) * kCoef;
765 template <
int Prec2,
typename RoundPolicy2>
766 static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
767 if constexpr (Prec > Prec2) {
769 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>, &result)) {
773 }
else if constexpr (Prec < Prec2) {
774 return FromUnbiased(impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
780 template <
int Prec2,
typename RoundPolicy2>
783 template <
typename T,
int OldPrec,
typename OldRound>
792struct IsDecimal : std::false_type {};
794template <
int Prec,
typename RoundPolicy>
795struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
816template <
typename T,
int OldPrec,
typename OldRound>
818 static_assert(kIsDecimal<T>);
819 return T::FromDecimal(arg);
827template <
int Prec,
typename RoundPolicy>
828constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after) {
829 using Dec =
Decimal<Prec, RoundPolicy>;
830 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
833 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
834 __builtin_add_overflow(result, after, &result)) {
838 return Dec::FromUnbiased(result);
842template <
int Prec,
typename RoundPolicy>
843constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
int original_precision) {
844 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
847 if (original_precision <= Prec) {
849 const int missing_digits = Prec - original_precision;
850 const int64_t factor =
Pow10(missing_digits
);
851 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
854 const int extra_digits = original_precision - Prec;
855 const int64_t factor =
Pow10(extra_digits
);
858 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
859 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
863template <
int Prec,
typename RoundPolicy>
864constexpr Decimal<Prec, RoundPolicy> FromUnpackedWithExponent(
867 int original_precision,
869 bool is_negative_exponent,
872 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
875 if (before == 0 && after == 0) {
876 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
879 const int effective_exponent = is_negative_exponent ? -exponent : exponent;
881 if (effective_exponent + Prec < -kMaxDecimalDigits) {
882 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
885 int total_scale = effective_exponent + Prec - original_precision;
887 if (before == 0 && after != 0) {
888 if (!is_negative_exponent) {
889 if (exponent >= leading_zeros) {
890 exponent -= leading_zeros;
891 original_precision -= leading_zeros;
893 original_precision -= exponent;
899 int64_t value = before;
901 if (__builtin_mul_overflow(value,
Pow10(original_precision
), &value) ||
902 __builtin_add_overflow(value, after, &value)) {
906 if (total_scale > 0) {
907 if (total_scale > kMaxDecimalDigits) {
910 if (__builtin_mul_overflow(value,
Pow10(total_scale
), &value)) {
913 }
else if (total_scale < 0) {
914 const int64_t divisor =
Pow10(-total_scale
);
915 value = Div<RoundPolicy>(value, divisor);
918 return Decimal<Prec, RoundPolicy>::FromUnbiased(value);
921struct UnpackedDecimal {
929template <
int Prec,
typename RoundPolicy>
930constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
931 using Dec =
Decimal<Prec, RoundPolicy>;
932 return {dec.AsUnbiased() / Dec::kDecimalFactor, dec.AsUnbiased() % Dec::kDecimalFactor};
938template <
int Prec,
typename RoundPolicy>
939UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
940 if (new_prec == Prec) {
941 return AsUnpacked(dec);
944 if (new_prec > Prec) {
945 if (__builtin_mul_overflow(dec.AsUnbiased(),
Pow10(new_prec - Prec
), &result)) {
949 result = impl::Div<RoundPolicy>(dec.AsUnbiased(),
Pow10(Prec - new_prec
));
951 const auto dec_factor =
Pow10(new_prec
);
952 return {result / dec_factor, result % dec_factor};
955template <
typename CharT>
956constexpr bool IsSpace(CharT c) {
957 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
960template <
typename CharT,
typename Traits>
961class StringCharSequence {
963 explicit constexpr StringCharSequence(std::basic_string_view<CharT, Traits> sv)
964 : current_(sv.begin()), end_(sv.end()) {}
967 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
969 constexpr void Unget() { --current_; }
972 typename std::basic_string_view<CharT, Traits>::iterator current_;
973 typename std::basic_string_view<CharT, Traits>::iterator end_;
976template <
typename CharT,
typename Traits>
977class StreamCharSequence {
979 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in) : in_(&in) {}
983 constexpr CharT kEof = std::basic_istream<CharT, Traits>::traits_type::eof();
987 const CharT c = in_->peek();
995 void Unget() { in_->unget(); }
998 std::basic_istream<CharT, Traits>* in_;
1001enum class ParseOptions {
1006 kAllowSpaces = 1 << 0,
1010 kAllowTrailingJunk = 1 << 1,
1014 kAllowBoundaryDot = 1 << 2,
1018 kAllowRounding = 1 << 3,
1022 kAllowExponent = 1 << 4
1025enum class ParseErrorCode : uint8_t {
1049 kExponentNotAllowed,
1056struct ParseUnpackedResult {
1059 uint8_t decimal_digits{0};
1060 bool is_negative{
false};
1061 uint8_t exponent{0};
1062 bool is_negative_exponent{
false};
1063 std::optional<ParseErrorCode> error;
1064 uint32_t error_position{-1U};
1065 int zeros_after_dec{0};
1068enum class ParseState {
1103constexpr inline void
1104StateToExpSign(std::optional<ParseErrorCode>& error, ParseState& state,
utils::Flags<ParseOptions>& options) {
1105 if (!error && !(options & ParseOptions::kAllowExponent)) {
1106 error = ParseErrorCode::kExponentNotAllowed;
1108 state = ParseState::kExpSign;
1112template <
typename CharSequence>
1113[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(CharSequence input,
utils::Flags<ParseOptions> options) {
1114 constexpr char dec_point =
'.';
1118 bool is_negative =
false;
1120 ptrdiff_t position = -1;
1121 auto state = ParseState::kSign;
1122 std::optional<ParseErrorCode> error;
1123 int before_digit_count = 0;
1124 uint8_t after_digit_count = 0;
1125 bool is_negative_exp =
false;
1126 uint8_t exponent = 0;
1127 int zeros_after_dec = 0;
1129 while (state != ParseState::kEnd) {
1130 const auto c = input.Get();
1131 if (c ==
'\0')
break;
1132 if (!error) ++position;
1135 case ParseState::kSign:
1138 state = ParseState::kBeforeFirstDig;
1139 }
else if (c ==
'+') {
1140 state = ParseState::kBeforeFirstDig;
1141 }
else if (c ==
'0') {
1142 state = ParseState::kLeadingZeros;
1143 before_digit_count = 1;
1144 }
else if ((c >=
'1') && (c <=
'9')) {
1145 state = ParseState::kBeforeDec;
1146 before =
static_cast<
int>(c -
'0');
1147 before_digit_count = 1;
1148 }
else if (c == dec_point) {
1149 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1150 error = ParseErrorCode::kBoundaryDot;
1152 state = ParseState::kZerosAfterDec;
1153 }
else if (IsSpace(c)) {
1154 if (!(options & ParseOptions::kAllowSpaces)) {
1155 state = ParseState::kEnd;
1156 error = ParseErrorCode::kSpace;
1159 state = ParseState::kEnd;
1160 error = ParseErrorCode::kWrongChar;
1163 case ParseState::kBeforeFirstDig:
1165 state = ParseState::kLeadingZeros;
1166 before_digit_count = 1;
1167 }
else if ((c >=
'1') && (c <=
'9')) {
1168 state = ParseState::kBeforeDec;
1169 before =
static_cast<
int>(c -
'0');
1170 before_digit_count = 1;
1171 }
else if (c == dec_point) {
1172 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1173 error = ParseErrorCode::kBoundaryDot;
1175 state = ParseState::kAfterDec;
1177 state = ParseState::kEnd;
1178 error = ParseErrorCode::kWrongChar;
1181 case ParseState::kLeadingZeros:
1184 }
else if ((c >=
'1') && (c <=
'9')) {
1185 state = ParseState::kBeforeDec;
1186 before =
static_cast<
int>(c -
'0');
1187 }
else if (c == dec_point) {
1188 state = ParseState::kZerosAfterDec;
1189 }
else if (c ==
'e' || c ==
'E') {
1190 StateToExpSign( error, state, options);
1192 state = ParseState::kEnd;
1195 case ParseState::kBeforeDec:
1196 if ((c >=
'0') && (c <=
'9')) {
1197 if (before_digit_count < kMaxDecimalDigits) {
1198 before = 10 * before +
static_cast<
int>(c -
'0');
1199 before_digit_count++;
1200 }
else if (!error) {
1201 error = ParseErrorCode::kOverflow;
1203 }
else if (c == dec_point) {
1204 state = ParseState::kZerosAfterDec;
1205 }
else if (c ==
'e' || c ==
'E') {
1206 StateToExpSign( error, state, options);
1208 state = ParseState::kEnd;
1212 case ParseState::kZerosAfterDec:
1215 after_digit_count++;
1220 case ParseState::kAfterDec:
1221 state = ParseState::kAfterDec;
1222 if ((c >=
'0') && (c <=
'9')) {
1223 if (after_digit_count < kMaxDecimalDigits) {
1224 after = 10 * after +
static_cast<
int>(c -
'0');
1225 after_digit_count++;
1227 if (!(options & ParseOptions::kAllowRounding) && !error) {
1228 error = ParseErrorCode::kRounding;
1230 state = ParseState::kIgnoringAfterDec;
1237 }
else if (c ==
'e' || c ==
'E') {
1238 StateToExpSign( error, state, options);
1240 if (!(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0 && !error) {
1241 error = ParseErrorCode::kBoundaryDot;
1243 state = ParseState::kEnd;
1247 case ParseState::kIgnoringAfterDec:
1248 if ((c >=
'0') && (c <=
'9')) {
1250 }
else if (c ==
'e' || c ==
'E') {
1251 StateToExpSign( error, state, options);
1253 state = ParseState::kEnd;
1257 case ParseState::kExpSign:
1259 is_negative_exp =
false;
1260 state = ParseState::kExpFirstDigit;
1261 }
else if (c ==
'-') {
1262 is_negative_exp =
true;
1263 state = ParseState::kExpFirstDigit;
1264 }
else if (c >=
'0' && c <=
'9') {
1265 exponent =
static_cast<
int>(c -
'0');
1266 state = ParseState::kExpDigits;
1269 error = ParseErrorCode::kWrongChar;
1271 state = ParseState::kEnd;
1275 case ParseState::kExpFirstDigit:
1276 if (c >=
'0' && c <=
'9') {
1277 exponent =
static_cast<
int>(c -
'0');
1278 state = ParseState::kExpDigits;
1280 state = ParseState::kEnd;
1281 if (!error) error = ParseErrorCode::kWrongChar;
1285 case ParseState::kExpDigits:
1286 if (c >=
'0' && c <=
'9') {
1287 if ((__builtin_mul_overflow(10, exponent, &exponent) ||
1288 __builtin_add_overflow(
static_cast<
int>(c -
'0'), exponent, &exponent))) {
1289 if (is_negative_exp && !(options & ParseOptions::kAllowRounding) && !error) {
1290 error = ParseErrorCode::kRounding;
1292 if (!is_negative_exp && !error) {
1293 error = ParseErrorCode::kOverflow;
1297 state = ParseState::kEnd;
1301 case ParseState::kEnd:
1307 if (state == ParseState::kEnd) {
1310 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1311 if (!(options & ParseOptions::kAllowSpaces)) {
1312 error = ParseErrorCode::kSpace;
1317 const auto c = input.Get();
1318 if (c ==
'\0')
break;
1321 error = ParseErrorCode::kTrailingJunk;
1329 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1330 error = ParseErrorCode::kNoDigits;
1333 if (!error && state == ParseState::kZerosAfterDec && !(options & ParseOptions::kAllowBoundaryDot) &&
1334 after_digit_count == 0) {
1335 error = ParseErrorCode::kBoundaryDot;
1338 if ((!error && state == ParseState::kExpSign) || (!error && state == ParseState::kExpFirstDigit)) {
1339 error = ParseErrorCode::kNoExponentDigits;
1342 if (zeros_after_dec >= kMaxDecimalDigits) {
1343 after_digit_count = 0;
1345 zeros_after_dec = 0;
1356 static_cast<uint32_t>(position),
1362template <
int Prec,
typename RoundPolicy>
1364 Decimal<Prec, RoundPolicy> decimal;
1365 std::optional<ParseErrorCode> error;
1366 uint32_t error_position{-1U};
1370template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1371[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(CharSequence input,
utils::Flags<ParseOptions> options) {
1372 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1375 return {{}, parsed.error, parsed.error_position};
1378 if (!(options & ParseOptions::kAllowRounding) && parsed.decimal_digits > Prec) {
1379 return {{}, ParseErrorCode::kRounding, 0};
1382 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1383 return {{}, ParseErrorCode::kOverflow, 0};
1386 if (parsed.after == 0 && parsed.decimal_digits > 0) {
1388 parsed.decimal_digits = 0;
1389 parsed.zeros_after_dec = 0;
1392 if (parsed.is_negative) {
1393 parsed.before = -parsed.before;
1394 parsed.after = -parsed.after;
1397 if (parsed.exponent != 0) {
1399 FromUnpackedWithExponent<Prec, RoundPolicy>(
1402 parsed.decimal_digits,
1404 parsed.is_negative_exponent,
1405 parsed.zeros_after_dec
1412 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after, parsed.decimal_digits), {}, 0};
1415std::string GetErrorMessage(std::string_view source, std::string_view path, size_t position, ParseErrorCode reason);
1419void TrimTrailingZeros(int64_t& after,
int& after_precision);
1421std::string ToString(int64_t before, int64_t after,
int precision,
const FormatOptions& format_options);
1425template <
int Prec,
typename RoundPolicy>
1427 const auto result = impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(value), impl::ParseOptions::kNone);
1430 throw ParseError(impl::GetErrorMessage(value,
"<string>", result.error_position, *result.error));
1432 *
this = result.decimal;
1435template <
int Prec,
typename RoundPolicy>
1437 const auto result = impl::Parse<Prec, RoundPolicy>(
1438 impl::StringCharSequence(input),
1439 {impl::ParseOptions::kAllowSpaces,
1440 impl::ParseOptions::kAllowBoundaryDot,
1441 impl::ParseOptions::kAllowRounding,
1442 impl::ParseOptions::kAllowExponent}
1446 throw ParseError(impl::GetErrorMessage(input,
"<string>", result.error_position, *result.error));
1448 return result.decimal;
1459template <
int Prec,
typename RoundPolicy>
1461 return fmt::to_string(dec);
1475template <
int Prec,
typename RoundPolicy>
1477 auto precision = format_options
.precision.value_or(Prec);
1479 precision = std::min(precision, Prec);
1481 auto [before, after] = impl::AsUnpacked(dec, precision);
1482 return impl::ToString(before, after, precision, format_options);
1493template <
int Prec,
typename RoundPolicy>
1495 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1506template <
int NewPrec,
int Prec,
typename RoundPolicy>
1508 return ToStringTrailingZeros(
decimal64::decimal_cast<
Decimal<NewPrec, RoundPolicy>>(dec));
1526template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1527std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1528 if (is.flags() & std::ios_base::skipws) {
1531 const auto result = impl::Parse<Prec, RoundPolicy>(
1532 impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk, impl::ParseOptions::kAllowExponent}
1536 is.setstate(std::ios_base::failbit);
1545template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1546std::basic_ostream<CharT, Traits>&
1547operator<<(std::basic_ostream<CharT, Traits>& os,
const Decimal<Prec, RoundPolicy>& d) {
1554template <
int Prec,
typename RoundPolicy>
1562template <
int Prec,
typename RoundPolicy,
typename Value>
1565 const std::string input = value.
template As<std::string>();
1568 impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(std::string_view{input}), impl::ParseOptions::kNone);
1571 throw ParseError(impl::GetErrorMessage(input, value.GetPath(), result.error_position, *result.error));
1573 return result.decimal;
1578template <
int Prec,
typename RoundPolicy,
typename TargetType>
1580 return typename TargetType::Builder(ToString(object)).ExtractValue();
1585template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1587 WriteToStream(ToString(object), sw);
1591template <
int Prec,
typename RoundPolicy>
1598USERVER_NAMESPACE_END
1601template <
int Prec,
typename RoundPolicy>
1605 std::size_t operator()(
const Decimal& v)
const noexcept {
return std::hash<int64_t>{}(v.AsUnbiased()); }
1616template <
int Prec,
typename RoundPolicy,
typename Char>
1619 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1620 const auto* it = ctx.begin();
1621 const auto* end = ctx.end();
1623 if (it != end && *it ==
'.') {
1624 remove_trailing_zeros_ =
false;
1625 custom_precision_ = 0;
1627 while (it != end && *it >=
'0' && *it <=
'9') {
1628 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1633 if (!custom_precision_ && it != end && *it ==
'f') {
1634 remove_trailing_zeros_ =
false;
1638 if (it != end && *it !=
'}') {
1639 throw format_error(
"invalid format");
1645 template <
typename FormatContext>
1646 auto format(
const USERVER_NAMESPACE::
decimal64::
Decimal<Prec, RoundPolicy>& dec, FormatContext& ctx)
const {
1647 int after_digits = custom_precision_.value_or(Prec);
1648 auto [before, after] = USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1649 if (remove_trailing_zeros_) {
1650 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after, after_digits);
1653 if (after_digits > 0) {
1654 if (dec.Sign() == -1) {
1655 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before, -after, after_digits);
1657 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before, after, after_digits);
1660 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1665 bool remove_trailing_zeros_ =
true;
1666 std::optional<
int> custom_precision_;