443 using RoundPolicy = RoundPolicy_;
452 template <
typename Int, impl::EnableIfInt<Int> = 0>
467 explicit constexpr Decimal(std::string_view value);
476 template <
typename T>
478 static_assert(std::is_floating_point_v<T>);
481 static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
482 static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
484 const auto unbiased_float =
static_cast<
long double>(value) *
kDecimalFactor;
485 if (unbiased_float < impl::kMinRepresentableLongDouble || unbiased_float > impl::kMaxRepresentableLongDouble) {
510 result.value_ = value;
526 const int exponent_for_pack = Prec - original_precision;
528 if (exponent_for_pack >= 0) {
541 *
this = FromDecimal(rhs);
546 template <
typename Int, impl::EnableIfInt<Int> = 0>
552#ifdef USERVER_IMPL_HAS_THREE_WAY_COMPARISON
553 constexpr auto operator<=>(
const Decimal& rhs)
const =
default;
568 constexpr Decimal operator+()
const {
return *
this; }
570 constexpr Decimal operator-()
const {
576 constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs)
const {
577 if constexpr (Prec2 > Prec) {
578 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
579 }
else if constexpr (Prec2 < Prec) {
580 return *
this + FromDecimal(rhs);
583 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
590 template <
typename Int, impl::EnableIfInt<Int> = 0>
591 constexpr Decimal operator+(Int rhs)
const {
595 template <
typename Int, impl::EnableIfInt<Int> = 0>
602 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
607 template <
typename Int, impl::EnableIfInt<Int> = 0>
608 constexpr Decimal& operator+=(Int rhs) {
614 constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs)
const {
615 if constexpr (Prec2 > Prec) {
616 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
617 }
else if constexpr (Prec2 < Prec) {
618 return *
this - FromDecimal(rhs);
621 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
628 template <
typename Int, impl::EnableIfInt<Int> = 0>
629 constexpr Decimal operator-(Int rhs)
const {
633 template <
typename Int, impl::EnableIfInt<Int> = 0>
640 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
645 template <
typename Int, impl::EnableIfInt<Int> = 0>
646 constexpr Decimal& operator-=(Int rhs) {
651 template <
typename Int,
typename = impl::EnableIfInt<Int>>
652 constexpr Decimal operator*(Int rhs)
const {
654 if (rhs > impl::kMaxInt64 || __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
660 template <
typename Int, impl::EnableIfInt<Int> = 0>
665 template <
typename Int, impl::EnableIfInt<Int> = 0>
666 constexpr Decimal& operator*=(Int rhs) {
672 constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs)
const {
682 template <
typename Int,
typename = impl::EnableIfInt<Int>>
683 constexpr Decimal operator/(Int rhs)
const {
687 template <
typename Int,
typename = impl::EnableIfInt<Int>>
692 template <
typename Int,
typename = impl::EnableIfInt<Int>>
693 constexpr Decimal& operator/=(Int rhs) {
699 constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs)
const {
710 constexpr int Sign()
const {
return impl::Sign(value_); }
733 constexpr std::int64_t kLossLimit = (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
735 if (value_ > -kLossLimit && value_ < kLossLimit) {
739 constexpr int kCoef =
741 std::numeric_limits<std::int64_t>::digits - std::numeric_limits<
double>::digits - 3 * Prec, 0
762 template <
int Prec2,
typename RoundPolicy2>
763 static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
764 if constexpr (Prec > Prec2) {
766 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>, &result)) {
770 }
else if constexpr (Prec < Prec2) {
771 return FromUnbiased(impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
777 template <
int Prec2,
typename RoundPolicy2>
780 template <
typename T,
int OldPrec,
typename OldRound>
789struct IsDecimal : std::false_type {};
791template <
int Prec,
typename RoundPolicy>
792struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
813template <
typename T,
int OldPrec,
typename OldRound>
815 static_assert(kIsDecimal<T>);
816 return T::FromDecimal(arg);
824template <
int Prec,
typename RoundPolicy>
825constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after) {
826 using Dec =
Decimal<Prec, RoundPolicy>;
827 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
830 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
831 __builtin_add_overflow(result, after, &result)) {
835 return Dec::FromUnbiased(result);
839template <
int Prec,
typename RoundPolicy>
840constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
int original_precision) {
841 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
844 if (original_precision <= Prec) {
846 const int missing_digits = Prec - original_precision;
847 const int64_t factor =
Pow10(missing_digits
);
848 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
851 const int extra_digits = original_precision - Prec;
852 const int64_t factor =
Pow10(extra_digits
);
855 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
856 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
860struct UnpackedDecimal {
868template <
int Prec,
typename RoundPolicy>
869constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
870 using Dec =
Decimal<Prec, RoundPolicy>;
871 return {dec.AsUnbiased() / Dec::kDecimalFactor, dec.AsUnbiased() % Dec::kDecimalFactor};
877template <
int Prec,
typename RoundPolicy>
878UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
879 if (new_prec == Prec) {
880 return AsUnpacked(dec);
883 if (new_prec > Prec) {
884 if (__builtin_mul_overflow(dec.AsUnbiased(),
Pow10(new_prec - Prec
), &result)) {
888 result = impl::Div<RoundPolicy>(dec.AsUnbiased(),
Pow10(Prec - new_prec
));
890 const auto dec_factor =
Pow10(new_prec
);
891 return {result / dec_factor, result % dec_factor};
894template <
typename CharT>
895constexpr bool IsSpace(CharT c) {
896 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
899template <
typename CharT,
typename Traits>
900class StringCharSequence {
902 explicit constexpr StringCharSequence(std::basic_string_view<CharT, Traits> sv)
903 : current_(sv.begin()), end_(sv.end()) {}
906 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
908 constexpr void Unget() { --current_; }
911 typename std::basic_string_view<CharT, Traits>::iterator current_;
912 typename std::basic_string_view<CharT, Traits>::iterator end_;
915template <
typename CharT,
typename Traits>
916class StreamCharSequence {
918 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in) : in_(&in) {}
922 constexpr CharT kEof = std::basic_istream<CharT, Traits>::traits_type::eof();
926 const CharT c = in_->peek();
934 void Unget() { in_->unget(); }
937 std::basic_istream<CharT, Traits>* in_;
940enum class ParseOptions {
945 kAllowSpaces = 1 << 0,
949 kAllowTrailingJunk = 1 << 1,
953 kAllowBoundaryDot = 1 << 2,
957 kAllowRounding = 1 << 3
960enum class ParseErrorCode : uint8_t {
984struct ParseUnpackedResult {
987 uint8_t decimal_digits{0};
988 bool is_negative{
false};
989 std::optional<ParseErrorCode> error;
990 uint32_t error_position{-1U};
993enum class ParseState {
1017template <
typename CharSequence>
1018[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(CharSequence input,
utils::Flags<ParseOptions> options) {
1019 constexpr char dec_point =
'.';
1023 bool is_negative =
false;
1025 ptrdiff_t position = -1;
1026 auto state = ParseState::kSign;
1027 std::optional<ParseErrorCode> error;
1028 int before_digit_count = 0;
1029 uint8_t after_digit_count = 0;
1031 while (state != ParseState::kEnd) {
1032 const auto c = input.Get();
1033 if (c ==
'\0')
break;
1034 if (!error) ++position;
1037 case ParseState::kSign:
1040 state = ParseState::kBeforeFirstDig;
1041 }
else if (c ==
'+') {
1042 state = ParseState::kBeforeFirstDig;
1043 }
else if (c ==
'0') {
1044 state = ParseState::kLeadingZeros;
1045 before_digit_count = 1;
1046 }
else if ((c >=
'1') && (c <=
'9')) {
1047 state = ParseState::kBeforeDec;
1048 before =
static_cast<
int>(c -
'0');
1049 before_digit_count = 1;
1050 }
else if (c == dec_point) {
1051 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1052 error = ParseErrorCode::kBoundaryDot;
1054 state = ParseState::kAfterDec;
1055 }
else if (IsSpace(c)) {
1056 if (!(options & ParseOptions::kAllowSpaces)) {
1057 state = ParseState::kEnd;
1058 error = ParseErrorCode::kSpace;
1061 state = ParseState::kEnd;
1062 error = ParseErrorCode::kWrongChar;
1065 case ParseState::kBeforeFirstDig:
1067 state = ParseState::kLeadingZeros;
1068 before_digit_count = 1;
1069 }
else if ((c >=
'1') && (c <=
'9')) {
1070 state = ParseState::kBeforeDec;
1071 before =
static_cast<
int>(c -
'0');
1072 before_digit_count = 1;
1073 }
else if (c == dec_point) {
1074 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1075 error = ParseErrorCode::kBoundaryDot;
1077 state = ParseState::kAfterDec;
1079 state = ParseState::kEnd;
1080 error = ParseErrorCode::kWrongChar;
1083 case ParseState::kLeadingZeros:
1086 }
else if ((c >=
'1') && (c <=
'9')) {
1087 state = ParseState::kBeforeDec;
1088 before =
static_cast<
int>(c -
'0');
1089 }
else if (c == dec_point) {
1090 state = ParseState::kAfterDec;
1092 state = ParseState::kEnd;
1095 case ParseState::kBeforeDec:
1096 if ((c >=
'0') && (c <=
'9')) {
1097 if (before_digit_count < kMaxDecimalDigits) {
1098 before = 10 * before +
static_cast<
int>(c -
'0');
1099 before_digit_count++;
1100 }
else if (!error) {
1101 error = ParseErrorCode::kOverflow;
1103 }
else if (c == dec_point) {
1104 state = ParseState::kAfterDec;
1106 state = ParseState::kEnd;
1109 case ParseState::kAfterDec:
1110 if ((c >=
'0') && (c <=
'9')) {
1111 if (after_digit_count < kMaxDecimalDigits) {
1112 after = 10 * after +
static_cast<
int>(c -
'0');
1113 after_digit_count++;
1115 if (!(options & ParseOptions::kAllowRounding) && !error) {
1116 error = ParseErrorCode::kRounding;
1118 state = ParseState::kIgnoringAfterDec;
1125 if (!(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0 && !error) {
1126 error = ParseErrorCode::kBoundaryDot;
1128 state = ParseState::kEnd;
1131 case ParseState::kIgnoringAfterDec:
1132 if ((c >=
'0') && (c <=
'9')) {
1135 state = ParseState::kEnd;
1138 case ParseState::kEnd:
1144 if (state == ParseState::kEnd) {
1147 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1148 if (!(options & ParseOptions::kAllowSpaces)) {
1149 error = ParseErrorCode::kSpace;
1154 const auto c = input.Get();
1155 if (c ==
'\0')
break;
1158 error = ParseErrorCode::kTrailingJunk;
1166 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1167 error = ParseErrorCode::kNoDigits;
1170 if (!error && state == ParseState::kAfterDec && !(options & ParseOptions::kAllowBoundaryDot) &&
1171 after_digit_count == 0) {
1172 error = ParseErrorCode::kBoundaryDot;
1175 return {before, after, after_digit_count, is_negative, error,
static_cast<uint32_t>(position)};
1178template <
int Prec,
typename RoundPolicy>
1180 Decimal<Prec, RoundPolicy> decimal;
1181 std::optional<ParseErrorCode> error;
1182 uint32_t error_position{-1U};
1186template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1187[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(CharSequence input,
utils::Flags<ParseOptions> options) {
1188 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1191 return {{}, parsed.error, parsed.error_position};
1194 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1195 return {{}, ParseErrorCode::kOverflow, 0};
1198 if (!(options & ParseOptions::kAllowRounding) && parsed.decimal_digits > Prec) {
1199 return {{}, ParseErrorCode::kRounding, 0};
1202 if (parsed.is_negative) {
1203 parsed.before = -parsed.before;
1204 parsed.after = -parsed.after;
1207 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after, parsed.decimal_digits), {}, 0};
1210std::string GetErrorMessage(std::string_view source, std::string_view path, size_t position, ParseErrorCode reason);
1214void TrimTrailingZeros(int64_t& after,
int& after_precision);
1216std::string ToString(int64_t before, int64_t after,
int precision,
const FormatOptions& format_options);
1220template <
int Prec,
typename RoundPolicy>
1222 const auto result = impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(value), impl::ParseOptions::kNone);
1225 throw ParseError(impl::GetErrorMessage(value,
"<string>", result.error_position, *result.error));
1227 *
this = result.decimal;
1230template <
int Prec,
typename RoundPolicy>
1232 const auto result = impl::Parse<Prec, RoundPolicy>(
1233 impl::StringCharSequence(input),
1234 {impl::ParseOptions::kAllowSpaces, impl::ParseOptions::kAllowBoundaryDot, impl::ParseOptions::kAllowRounding}
1238 throw ParseError(impl::GetErrorMessage(input,
"<string>", result.error_position, *result.error));
1240 return result.decimal;
1251template <
int Prec,
typename RoundPolicy>
1253 return fmt::to_string(dec);
1267template <
int Prec,
typename RoundPolicy>
1269 auto precision = format_options
.precision.value_or(Prec);
1271 precision = std::min(precision, Prec);
1273 auto [before, after] = impl::AsUnpacked(dec, precision);
1274 return impl::ToString(before, after, precision, format_options);
1285template <
int Prec,
typename RoundPolicy>
1287 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1298template <
int NewPrec,
int Prec,
typename RoundPolicy>
1300 return ToStringTrailingZeros(
decimal64::decimal_cast<
Decimal<NewPrec, RoundPolicy>>(dec));
1317template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1318std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1319 if (is.flags() & std::ios_base::skipws) {
1323 impl::Parse<Prec, RoundPolicy>(impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk});
1326 is.setstate(std::ios_base::failbit);
1335template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1336std::basic_ostream<CharT, Traits>&
1337operator<<(std::basic_ostream<CharT, Traits>& os,
const Decimal<Prec, RoundPolicy>& d) {
1318std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
…}
1344template <
int Prec,
typename RoundPolicy>
1352template <
int Prec,
typename RoundPolicy,
typename Value>
1355 const std::string input = value.
template As<std::string>();
1358 impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(std::string_view{input}), impl::ParseOptions::kNone);
1361 throw ParseError(impl::GetErrorMessage(input, value.GetPath(), result.error_position, *result.error));
1363 return result.decimal;
1368template <
int Prec,
typename RoundPolicy,
typename TargetType>
1370 return typename TargetType::Builder(ToString(object)).ExtractValue();
1375template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1377 WriteToStream(ToString(object), sw);
1381template <
int Prec,
typename RoundPolicy>
1388USERVER_NAMESPACE_END
1391template <
int Prec,
typename RoundPolicy>
1395 std::size_t operator()(
const Decimal& v)
const noexcept {
return std::hash<int64_t>{}(v.AsUnbiased()); }
1406template <
int Prec,
typename RoundPolicy,
typename Char>
1409 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1410 const auto* it = ctx.begin();
1411 const auto* end = ctx.end();
1413 if (it != end && *it ==
'.') {
1414 remove_trailing_zeros_ =
false;
1415 custom_precision_ = 0;
1417 while (it != end && *it >=
'0' && *it <=
'9') {
1418 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1423 if (!custom_precision_ && it != end && *it ==
'f') {
1424 remove_trailing_zeros_ =
false;
1428 if (it != end && *it !=
'}') {
1429 throw format_error(
"invalid format");
1435 template <
typename FormatContext>
1436 auto format(
const USERVER_NAMESPACE::
decimal64::
Decimal<Prec, RoundPolicy>& dec, FormatContext& ctx)
const {
1437 int after_digits = custom_precision_.value_or(Prec);
1438 auto [before, after] = USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1439 if (remove_trailing_zeros_) {
1440 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after, after_digits);
1443 if (after_digits > 0) {
1444 if (dec.Sign() == -1) {
1445 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before, -after, after_digits);
1447 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before, after, after_digits);
1450 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1455 bool remove_trailing_zeros_ =
true;
1456 std::optional<
int> custom_precision_;