35#include <fmt/compile.h>
36#include <fmt/format.h>
38#include <userver/decimal64/format_options.hpp>
39#include <userver/formats/common/meta.hpp>
40#include <userver/utils/assert.hpp>
41#include <userver/utils/flags.hpp>
42#include <userver/utils/meta_light.hpp>
44USERVER_NAMESPACE_BEGIN
58 using std::runtime_error::runtime_error;
76 DivisionByZeroError();
81inline constexpr auto kMaxInt64 = std::numeric_limits<int64_t>::max();
82inline constexpr auto kMinInt64 = std::numeric_limits<int64_t>::min();
86inline constexpr auto kMinRepresentableLongDouble =
87 static_cast<
long double>(impl::kMinInt64) * (1 - 2 * std::numeric_limits<
long double>::epsilon());
88inline constexpr auto kMaxRepresentableLongDouble =
89 static_cast<
long double>(impl::kMaxInt64) * (1 - 2 * std::numeric_limits<
long double>::epsilon());
92using EnableIfInt = std::enable_if_t<meta::kIsInteger<T>,
int>;
95using EnableIfFloat = std::enable_if_t<std::is_floating_point_v<T>,
int>;
98constexpr std::array<int64_t, MaxExp + 1> PowSeries(int64_t base) {
100 std::array<int64_t, MaxExp + 1> result{};
101 for (
int i = 0; i < MaxExp; ++i) {
103 if (pow > kMaxInt64 / base) {
108 result[MaxExp] = pow;
112inline constexpr int kMaxDecimalDigits = 18;
113inline constexpr auto kPowSeries10 = PowSeries<kMaxDecimalDigits>(10);
116static_assert(kMaxInt64 / 10 < kPowSeries10[kMaxDecimalDigits]);
118template <
typename RoundPolicy>
119constexpr int64_t Div(int64_t nominator, int64_t denominator,
bool extra_odd_quotient =
false) {
122 if (denominator == -1) {
127 return RoundPolicy::DivRounded(nominator, denominator, extra_odd_quotient);
131template <
typename RoundPolicy>
132constexpr int64_t MulDiv(int64_t value1, int64_t value2, int64_t divisor) {
135#if __x86_64__
|| __ppc64__ || __aarch64__
136 using LongInt = __int128_t;
137 static_assert(
sizeof(
void*) == 8);
139 using LongInt = int64_t;
140 static_assert(
sizeof(
void*) == 4);
144 if constexpr (
sizeof(
void*) == 4) {
145 if (__builtin_mul_overflow(
static_cast<LongInt>(value1),
static_cast<LongInt>(value2), &prod)) {
149 prod =
static_cast<LongInt>(value1) * value2;
151 const auto whole = prod / divisor;
152 const auto rem =
static_cast<int64_t>(prod % divisor);
156 const auto whole64 =
static_cast<int64_t>(whole);
157 const bool extra_odd_quotient = whole64 % 2 != 0;
158 const int64_t rem_divided = Div<RoundPolicy>(rem, divisor, extra_odd_quotient);
159 UASSERT(rem_divided == -1 || rem_divided == 0 || rem_divided == 1);
161 return whole64 + rem_divided;
164constexpr int Sign(int64_t value) {
return (value > 0) - (value < 0); }
167constexpr int64_t Abs(int64_t value) {
169 return value >= 0 ? value : -value;
175constexpr int64_t Abs(T value) {
176 static_assert(std::is_floating_point_v<T>);
177 return value >= 0 ? value : -value;
183constexpr int64_t Floor(T value) {
184 if (
static_cast<int64_t>(value) <= value) {
185 return static_cast<int64_t>(value);
187 return static_cast<int64_t>(value) - 1;
194constexpr int64_t Ceil(T value) {
195 if (
static_cast<int64_t>(value) >= value) {
196 return static_cast<int64_t>(value);
198 return static_cast<int64_t>(value) + 1;
202template <
typename Int>
203constexpr int64_t ToInt64(Int value) {
204 static_assert(meta::kIsInteger<Int>);
205 static_assert(
sizeof(Int) <=
sizeof(int64_t));
207 if constexpr (
sizeof(Int) ==
sizeof(int64_t)) {
210 return static_cast<int64_t>(value);
213class HalfUpPolicy
final {
216 template <
typename T>
217 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
218 const T abs_remainder = abs -
static_cast<int64_t>(abs);
219 return abs_remainder >= 0.5;
223 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool ) {
224 const int64_t abs_a = impl::Abs(a);
225 const int64_t abs_b = impl::Abs(b);
226 const int64_t half_b = abs_b / 2;
227 const int64_t abs_remainder = abs_a % abs_b;
228 return abs_b % 2 == 0 ? abs_remainder >= half_b : abs_remainder > half_b;
232class HalfDownPolicy
final {
235 template <
typename T>
236 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
237 const T abs_remainder = abs -
static_cast<int64_t>(abs);
238 return abs_remainder > 0.5;
242 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool ) {
243 const int64_t abs_a = impl::Abs(a);
244 const int64_t abs_b = impl::Abs(b);
245 const int64_t half_b = abs_b / 2;
246 const int64_t abs_remainder = abs_a % abs_b;
247 return abs_remainder > half_b;
251class HalfEvenPolicy
final {
254 template <
typename T>
255 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
256 const T abs_remainder = abs -
static_cast<int64_t>(abs);
257 return abs_remainder == 0.5 ? impl::Floor(abs) % 2 != 0 : abs_remainder > 0.5;
261 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool extra_odd_quotient) {
262 const int64_t abs_a = impl::Abs(a);
263 const int64_t abs_b = impl::Abs(b);
264 const int64_t half_b = abs_b / 2;
265 const int64_t abs_remainder = abs_a % abs_b;
266 return (abs_b % 2 == 0 && abs_remainder == half_b) ? ((abs_a / abs_b) % 2 == 0) == extra_odd_quotient
267 : abs_remainder > half_b;
271template <
typename HalfPolicy>
272class HalfRoundPolicyBase {
274 template <
typename T>
275 [[nodiscard]]
static constexpr int64_t Round(T value) {
276 if ((value >= 0.0) == HalfPolicy::ShouldRoundAwayFromZero(value)) {
277 return impl::Ceil(value);
279 return impl::Floor(value);
283 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool extra_odd_quotient) {
284 if (HalfPolicy::ShouldRoundAwayFromZeroDiv(a, b, extra_odd_quotient)) {
285 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
286 return (a / b) + quotient_sign;
297 if (exp < 0 || exp > impl::kMaxDecimalDigits) {
298 throw std::runtime_error(
"Pow10: invalid power of 10");
300 return impl::kPowSeries10[
static_cast<size_t>(exp)];
311class DefRoundPolicy
final {
313 template <
typename T>
314 [[nodiscard]]
static constexpr int64_t Round(T value) {
315 return static_cast<int64_t>(value + (value < 0 ? -0.5 : 0.5));
318 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
319 const int64_t divisor_corr = impl::Abs(b / 2);
322 return (a + divisor_corr) / b;
325 return (a - divisor_corr) / b;
331class HalfDownRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfDownPolicy> {};
334class HalfUpRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfUpPolicy> {};
337class HalfEvenRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfEvenPolicy> {};
342 template <
typename T>
343 [[nodiscard]]
static constexpr int64_t Round(T value) {
344 return impl::Ceil(value);
347 [[
nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
348 const bool quotient_positive = (a >= 0) == (b >= 0);
349 return (a / b) + (a % b != 0 && quotient_positive);
354class FloorRoundPolicy
final {
356 template <
typename T>
357 [[nodiscard]]
static constexpr int64_t Round(T value) {
358 return impl::Floor(value);
361 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
362 const bool quotient_negative = (a < 0) != (b < 0);
363 return (a / b) - (a % b != 0 && quotient_negative);
368class RoundDownRoundPolicy
final {
370 template <
typename T>
371 [[nodiscard]]
static constexpr int64_t Round(T value) {
372 return static_cast<int64_t>(value);
375 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
381class RoundUpRoundPolicy
final {
383 template <
typename T>
384 [[nodiscard]]
static constexpr int64_t Round(T value) {
386 return impl::Ceil(value);
388 return impl::Floor(value);
392 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
393 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
394 return (a / b) + (a % b != 0) * quotient_sign;
435template <
int Prec,
typename RoundPolicy_ = DefRoundPolicy>
442 using RoundPolicy = RoundPolicy_;
451 template <
typename Int, impl::EnableIfInt<Int> = 0>
466 explicit constexpr Decimal(std::string_view value);
475 template <
typename T>
477 static_assert(std::is_floating_point_v<T>);
480 static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
481 static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
483 const auto unbiased_float =
static_cast<
long double>(value) *
kDecimalFactor;
484 if (unbiased_float < impl::kMinRepresentableLongDouble || unbiased_float > impl::kMaxRepresentableLongDouble) {
509 result.value_ = value;
524 static constexpr Decimal
FromBiased(int64_t original_unbiased,
int original_precision) {
525 const int exponent_for_pack = Prec - original_precision;
527 if (exponent_for_pack >= 0) {
528 return FromUnbiased(original_unbiased) * Pow10(exponent_for_pack);
530 return FromUnbiased(impl::Div<RoundPolicy>(original_unbiased, Pow10(-exponent_for_pack)));
539 Decimal&
operator=(Decimal<Prec2, RoundPolicy> rhs) {
540 *
this = FromDecimal(rhs);
545 template <
typename Int, impl::EnableIfInt<Int> = 0>
547 *
this = Decimal{rhs};
551#ifdef __cpp_lib_three_way_comparison
552 constexpr auto operator<=>(
const Decimal& rhs)
const =
default;
554 constexpr bool operator==(Decimal rhs)
const {
return value_ == rhs.value_; }
556 constexpr bool operator!=(Decimal rhs)
const {
return value_ != rhs.value_; }
558 constexpr bool operator<(Decimal rhs)
const {
return value_ < rhs.value_; }
560 constexpr bool operator<=(Decimal rhs)
const {
return value_ <= rhs.value_; }
562 constexpr bool operator>(Decimal rhs)
const {
return value_ > rhs.value_; }
564 constexpr bool operator>=(Decimal rhs)
const {
return value_ >= rhs.value_; }
567 constexpr Decimal operator+()
const {
return *
this; }
569 constexpr Decimal operator-()
const {
575 constexpr auto operator+(Decimal<Prec2, RoundPolicy> rhs)
const {
576 if constexpr (Prec2 > Prec) {
577 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
578 }
else if constexpr (Prec2 < Prec) {
579 return *
this + FromDecimal(rhs);
582 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
589 template <
typename Int, impl::EnableIfInt<Int> = 0>
590 constexpr Decimal operator+(Int rhs)
const {
591 return *
this + Decimal{rhs};
594 template <
typename Int, impl::EnableIfInt<Int> = 0>
595 friend constexpr Decimal operator+(Int lhs, Decimal rhs) {
596 return Decimal{lhs} + rhs;
600 constexpr Decimal& operator+=(Decimal<Prec2, RoundPolicy> rhs) {
601 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
606 template <
typename Int, impl::EnableIfInt<Int> = 0>
607 constexpr Decimal& operator+=(Int rhs) {
613 constexpr auto operator-(Decimal<Prec2, RoundPolicy> rhs)
const {
614 if constexpr (Prec2 > Prec) {
615 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
616 }
else if constexpr (Prec2 < Prec) {
617 return *
this - FromDecimal(rhs);
620 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
627 template <
typename Int, impl::EnableIfInt<Int> = 0>
628 constexpr Decimal operator-(Int rhs)
const {
629 return *
this - Decimal{rhs};
632 template <
typename Int, impl::EnableIfInt<Int> = 0>
633 friend constexpr Decimal operator-(Int lhs, Decimal rhs) {
634 return Decimal{lhs} - rhs;
638 constexpr Decimal& operator-=(Decimal<Prec2, RoundPolicy> rhs) {
639 static_assert(Prec2 <= Prec,
"Implicit cast to Decimal of lower precision in assignment");
644 template <
typename Int, impl::EnableIfInt<Int> = 0>
645 constexpr Decimal& operator-=(Int rhs) {
650 template <
typename Int,
typename = impl::EnableIfInt<Int>>
651 constexpr Decimal operator*(Int rhs)
const {
653 if (rhs > impl::kMaxInt64 || __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
659 template <
typename Int, impl::EnableIfInt<Int> = 0>
660 friend constexpr Decimal operator*(Int lhs, Decimal rhs) {
664 template <
typename Int, impl::EnableIfInt<Int> = 0>
665 constexpr Decimal& operator*=(Int rhs) {
671 constexpr Decimal operator*(Decimal<Prec2, RoundPolicy> rhs)
const {
676 constexpr Decimal& operator*=(Decimal<Prec2, RoundPolicy> rhs) {
681 template <
typename Int,
typename = impl::EnableIfInt<Int>>
682 constexpr Decimal operator/(Int rhs)
const {
686 template <
typename Int,
typename = impl::EnableIfInt<Int>>
687 friend constexpr Decimal operator/(Int lhs, Decimal rhs) {
688 return Decimal{lhs} / rhs;
691 template <
typename Int,
typename = impl::EnableIfInt<Int>>
692 constexpr Decimal& operator/=(Int rhs) {
698 constexpr Decimal operator/(Decimal<Prec2, RoundPolicy> rhs)
const {
703 constexpr Decimal& operator/=(Decimal<Prec2, RoundPolicy> rhs) {
709 constexpr int Sign()
const {
return impl::Sign(value_); }
717 return *
this / base.AsUnbiased() * base.AsUnbiased();
732 constexpr std::int64_t kLossLimit = (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
734 if (value_ > -kLossLimit && value_ < kLossLimit) {
738 constexpr int kCoef =
740 std::numeric_limits<std::int64_t>::digits - std::numeric_limits<
double>::digits - 3 * Prec, 0
761 template <
int Prec2,
typename RoundPolicy2>
762 static constexpr Decimal FromDecimal(Decimal<Prec2, RoundPolicy2> source) {
763 if constexpr (Prec > Prec2) {
765 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>, &result)) {
769 }
else if constexpr (Prec < Prec2) {
770 return FromUnbiased(impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
776 template <
int Prec2,
typename RoundPolicy2>
777 friend class Decimal;
779 template <
typename T,
int OldPrec,
typename OldRound>
780 friend constexpr T
decimal_cast(Decimal<OldPrec, OldRound> arg);
788struct IsDecimal : std::false_type {};
790template <
int Prec,
typename RoundPolicy>
791struct IsDecimal<Decimal<Prec, RoundPolicy>> : std::true_type {};
812template <
typename T,
int OldPrec,
typename OldRound>
814 static_assert(kIsDecimal<T>);
815 return T::FromDecimal(arg);
823template <
int Prec,
typename RoundPolicy>
824constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after) {
825 using Dec = Decimal<Prec, RoundPolicy>;
826 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
829 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
830 __builtin_add_overflow(result, after, &result)) {
834 return Dec::FromUnbiased(result);
838template <
int Prec,
typename RoundPolicy>
839constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
int original_precision) {
840 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
841 UASSERT(after > -Pow10(original_precision) && after < Pow10(original_precision));
843 if (original_precision <= Prec) {
845 const int missing_digits = Prec - original_precision;
846 const int64_t factor = Pow10(missing_digits);
847 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
850 const int extra_digits = original_precision - Prec;
851 const int64_t factor = Pow10(extra_digits);
854 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
855 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
859struct UnpackedDecimal {
867template <
int Prec,
typename RoundPolicy>
868constexpr UnpackedDecimal AsUnpacked(Decimal<Prec, RoundPolicy> dec) {
869 using Dec = Decimal<Prec, RoundPolicy>;
870 return {dec.AsUnbiased() / Dec::kDecimalFactor, dec.AsUnbiased() % Dec::kDecimalFactor};
876template <
int Prec,
typename RoundPolicy>
877UnpackedDecimal AsUnpacked(Decimal<Prec, RoundPolicy> dec,
int new_prec) {
878 if (new_prec == Prec) {
879 return AsUnpacked(dec);
882 if (new_prec > Prec) {
883 if (__builtin_mul_overflow(dec.AsUnbiased(), Pow10(new_prec - Prec), &result)) {
887 result = impl::Div<RoundPolicy>(dec.AsUnbiased(), Pow10(Prec - new_prec));
889 const auto dec_factor = Pow10(new_prec);
890 return {result / dec_factor, result % dec_factor};
893template <
typename CharT>
894constexpr bool IsSpace(CharT c) {
895 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
898template <
typename CharT,
typename Traits>
899class StringCharSequence {
901 explicit constexpr StringCharSequence(std::basic_string_view<CharT, Traits> sv)
902 : current_(sv.begin()), end_(sv.end()) {}
905 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
907 constexpr void Unget() { --current_; }
910 typename std::basic_string_view<CharT, Traits>::iterator current_;
911 typename std::basic_string_view<CharT, Traits>::iterator end_;
914template <
typename CharT,
typename Traits>
915class StreamCharSequence {
917 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in) : in_(&in) {}
921 constexpr CharT kEof = std::basic_istream<CharT, Traits>::traits_type::eof();
925 const CharT c = in_->peek();
933 void Unget() { in_->unget(); }
936 std::basic_istream<CharT, Traits>* in_;
939enum class ParseOptions {
944 kAllowSpaces = 1 << 0,
948 kAllowTrailingJunk = 1 << 1,
952 kAllowBoundaryDot = 1 << 2,
956 kAllowRounding = 1 << 3
959enum class ParseErrorCode : uint8_t {
983struct ParseUnpackedResult {
986 uint8_t decimal_digits{0};
987 bool is_negative{
false};
988 std::optional<ParseErrorCode> error;
989 uint32_t error_position{-1U};
992enum class ParseState {
1016template <
typename CharSequence>
1017[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(CharSequence input,
utils::Flags<ParseOptions> options) {
1018 constexpr char dec_point =
'.';
1022 bool is_negative =
false;
1024 ptrdiff_t position = -1;
1025 auto state = ParseState::kSign;
1026 std::optional<ParseErrorCode> error;
1027 int before_digit_count = 0;
1028 uint8_t after_digit_count = 0;
1030 while (state != ParseState::kEnd) {
1031 const auto c = input.Get();
1032 if (c ==
'\0')
break;
1033 if (!error) ++position;
1036 case ParseState::kSign:
1039 state = ParseState::kBeforeFirstDig;
1040 }
else if (c ==
'+') {
1041 state = ParseState::kBeforeFirstDig;
1042 }
else if (c ==
'0') {
1043 state = ParseState::kLeadingZeros;
1044 before_digit_count = 1;
1045 }
else if ((c >=
'1') && (c <=
'9')) {
1046 state = ParseState::kBeforeDec;
1047 before =
static_cast<
int>(c -
'0');
1048 before_digit_count = 1;
1049 }
else if (c == dec_point) {
1050 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1051 error = ParseErrorCode::kBoundaryDot;
1053 state = ParseState::kAfterDec;
1054 }
else if (IsSpace(c)) {
1055 if (!(options & ParseOptions::kAllowSpaces)) {
1056 state = ParseState::kEnd;
1057 error = ParseErrorCode::kSpace;
1060 state = ParseState::kEnd;
1061 error = ParseErrorCode::kWrongChar;
1064 case ParseState::kBeforeFirstDig:
1066 state = ParseState::kLeadingZeros;
1067 before_digit_count = 1;
1068 }
else if ((c >=
'1') && (c <=
'9')) {
1069 state = ParseState::kBeforeDec;
1070 before =
static_cast<
int>(c -
'0');
1071 before_digit_count = 1;
1072 }
else if (c == dec_point) {
1073 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1074 error = ParseErrorCode::kBoundaryDot;
1076 state = ParseState::kAfterDec;
1078 state = ParseState::kEnd;
1079 error = ParseErrorCode::kWrongChar;
1082 case ParseState::kLeadingZeros:
1085 }
else if ((c >=
'1') && (c <=
'9')) {
1086 state = ParseState::kBeforeDec;
1087 before =
static_cast<
int>(c -
'0');
1088 }
else if (c == dec_point) {
1089 state = ParseState::kAfterDec;
1091 state = ParseState::kEnd;
1094 case ParseState::kBeforeDec:
1095 if ((c >=
'0') && (c <=
'9')) {
1096 if (before_digit_count < kMaxDecimalDigits) {
1097 before = 10 * before +
static_cast<
int>(c -
'0');
1098 before_digit_count++;
1099 }
else if (!error) {
1100 error = ParseErrorCode::kOverflow;
1102 }
else if (c == dec_point) {
1103 state = ParseState::kAfterDec;
1105 state = ParseState::kEnd;
1108 case ParseState::kAfterDec:
1109 if ((c >=
'0') && (c <=
'9')) {
1110 if (after_digit_count < kMaxDecimalDigits) {
1111 after = 10 * after +
static_cast<
int>(c -
'0');
1112 after_digit_count++;
1114 if (!(options & ParseOptions::kAllowRounding) && !error) {
1115 error = ParseErrorCode::kRounding;
1117 state = ParseState::kIgnoringAfterDec;
1124 if (!(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0 && !error) {
1125 error = ParseErrorCode::kBoundaryDot;
1127 state = ParseState::kEnd;
1130 case ParseState::kIgnoringAfterDec:
1131 if ((c >=
'0') && (c <=
'9')) {
1134 state = ParseState::kEnd;
1137 case ParseState::kEnd:
1143 if (state == ParseState::kEnd) {
1146 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1147 if (!(options & ParseOptions::kAllowSpaces)) {
1148 error = ParseErrorCode::kSpace;
1153 const auto c = input.Get();
1154 if (c ==
'\0')
break;
1157 error = ParseErrorCode::kTrailingJunk;
1165 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1166 error = ParseErrorCode::kNoDigits;
1169 if (!error && state == ParseState::kAfterDec && !(options & ParseOptions::kAllowBoundaryDot) &&
1170 after_digit_count == 0) {
1171 error = ParseErrorCode::kBoundaryDot;
1174 return {before, after, after_digit_count, is_negative, error,
static_cast<uint32_t>(position)};
1177template <
int Prec,
typename RoundPolicy>
1179 Decimal<Prec, RoundPolicy> decimal;
1180 std::optional<ParseErrorCode> error;
1181 uint32_t error_position{-1U};
1185template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1186[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(CharSequence input,
utils::Flags<ParseOptions> options) {
1187 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1190 return {{}, parsed.error, parsed.error_position};
1193 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1194 return {{}, ParseErrorCode::kOverflow, 0};
1197 if (!(options & ParseOptions::kAllowRounding) && parsed.decimal_digits > Prec) {
1198 return {{}, ParseErrorCode::kRounding, 0};
1201 if (parsed.is_negative) {
1202 parsed.before = -parsed.before;
1203 parsed.after = -parsed.after;
1206 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after, parsed.decimal_digits), {}, 0};
1209std::string GetErrorMessage(std::string_view source, std::string_view path, size_t position, ParseErrorCode reason);
1213void TrimTrailingZeros(int64_t& after,
int& after_precision);
1215std::string ToString(int64_t before, int64_t after,
int precision,
const FormatOptions& format_options);
1219template <
int Prec,
typename RoundPolicy>
1220constexpr Decimal<Prec, RoundPolicy>::
Decimal(std::string_view value) {
1221 const auto result = impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(value), impl::ParseOptions::kNone);
1224 throw ParseError(impl::GetErrorMessage(value,
"<string>", result.error_position, *result.error));
1226 *
this = result.decimal;
1229template <
int Prec,
typename RoundPolicy>
1230constexpr Decimal<Prec, RoundPolicy> Decimal<Prec, RoundPolicy>::FromStringPermissive(std::string_view input) {
1231 const auto result = impl::Parse<Prec, RoundPolicy>(
1232 impl::StringCharSequence(input),
1233 {impl::ParseOptions::kAllowSpaces, impl::ParseOptions::kAllowBoundaryDot, impl::ParseOptions::kAllowRounding}
1237 throw ParseError(impl::GetErrorMessage(input,
"<string>", result.error_position, *result.error));
1239 return result.decimal;
1250template <
int Prec,
typename RoundPolicy>
1252 return fmt::to_string(dec);
1266template <
int Prec,
typename RoundPolicy>
1268 auto precision = format_options.precision.value_or(Prec);
1270 precision = std::min(precision, Prec);
1272 auto [before, after] = impl::AsUnpacked(dec, precision);
1273 return impl::ToString(before, after, precision, format_options);
1284template <
int Prec,
typename RoundPolicy>
1286 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1297template <
int NewPrec,
int Prec,
typename RoundPolicy>
1299 return ToStringTrailingZeros(
decimal64::decimal_cast<Decimal<NewPrec, RoundPolicy>>(dec));
1316template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1317std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is, Decimal<Prec, RoundPolicy>& d) {
1318 if (is.flags() & std::ios_base::skipws) {
1322 impl::Parse<Prec, RoundPolicy>(impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk});
1325 is.setstate(std::ios_base::failbit);
1334template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1335std::basic_ostream<CharT, Traits>&
1336operator<<(std::basic_ostream<CharT, Traits>& os,
const Decimal<Prec, RoundPolicy>& d) {
1343template <
int Prec,
typename RoundPolicy>
1344logging::LogHelper& operator<<(
logging::LogHelper& lh,
const Decimal<Prec, RoundPolicy>& d) {
1351template <
int Prec,
typename RoundPolicy,
typename Value>
1354 const std::string input = value.
template As<std::string>();
1357 impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(std::string_view{input}), impl::ParseOptions::kNone);
1360 throw ParseError(impl::GetErrorMessage(input, value.GetPath(), result.error_position, *result.error));
1362 return result.decimal;
1367template <
int Prec,
typename RoundPolicy,
typename TargetType>
1369 return typename TargetType::Builder(ToString(object)).ExtractValue();
1374template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1375void WriteToStream(
const Decimal<Prec, RoundPolicy>& object, StringBuilder& sw) {
1376 WriteToStream(ToString(object), sw);
1381USERVER_NAMESPACE_END
1384template <
int Prec,
typename RoundPolicy>
1385struct std::hash<USERVER_NAMESPACE::
decimal64::Decimal<Prec, RoundPolicy>> {
1386 using Decimal = USERVER_NAMESPACE::
decimal64::Decimal<Prec, RoundPolicy>;
1388 std::size_t operator()(
const Decimal& v)
const noexcept {
return std::hash<int64_t>{}(v.AsUnbiased()); }
1399template <
int Prec,
typename RoundPolicy,
typename Char>
1400class fmt::formatter<USERVER_NAMESPACE::decimal64::Decimal<Prec, RoundPolicy>, Char> {
1402 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1403 const auto* it = ctx.begin();
1404 const auto* end = ctx.end();
1406 if (it != end && *it ==
'.') {
1407 remove_trailing_zeros_ =
false;
1408 custom_precision_ = 0;
1410 while (it != end && *it >=
'0' && *it <=
'9') {
1411 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1416 if (!custom_precision_ && it != end && *it ==
'f') {
1417 remove_trailing_zeros_ =
false;
1421 if (it != end && *it !=
'}') {
1422 throw format_error(
"invalid format");
1428 template <
typename FormatContext>
1429 auto format(
const USERVER_NAMESPACE::decimal64::Decimal<Prec, RoundPolicy>& dec, FormatContext& ctx)
const {
1430 int after_digits = custom_precision_.value_or(Prec);
1431 auto [before, after] = USERVER_NAMESPACE::decimal64::impl::AsUnpacked(dec, after_digits);
1432 if (remove_trailing_zeros_) {
1433 USERVER_NAMESPACE::decimal64::impl::TrimTrailingZeros(after, after_digits);
1436 if (after_digits > 0) {
1437 if (dec.Sign() == -1) {
1438 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before, -after, after_digits);
1440 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before, after, after_digits);
1443 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1448 bool remove_trailing_zeros_ =
true;
1449 std::optional<
int> custom_precision_;