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();
85using EnableIfInt = std::enable_if_t<meta::kIsInteger<T>,
int>;
88using EnableIfFloat = std::enable_if_t<std::is_floating_point_v<T>,
int>;
91constexpr std::array<int64_t, MaxExp + 1> PowSeries(int64_t base) {
93 std::array<int64_t, MaxExp + 1> result{};
94 for (
int i = 0; i < MaxExp; ++i) {
96 if (pow > kMaxInt64 / base) {
101 result[MaxExp] = pow;
105inline constexpr int kMaxDecimalDigits = 18;
106inline constexpr auto kPowSeries10 = PowSeries<kMaxDecimalDigits>(10);
109static_assert(kMaxInt64 / 10 < kPowSeries10[kMaxDecimalDigits]);
111template <
typename RoundPolicy>
112constexpr int64_t Div(int64_t nominator, int64_t denominator,
113 bool extra_odd_quotient =
false) {
116 if (denominator == -1) {
121 return RoundPolicy::DivRounded(nominator, denominator, extra_odd_quotient);
125template <
typename RoundPolicy>
126constexpr int64_t MulDiv(int64_t value1, int64_t value2, int64_t divisor) {
129#if __x86_64__
|| __ppc64__ || __aarch64__
130 using LongInt = __int128_t;
131 static_assert(
sizeof(
void*) == 8);
133 using LongInt = int64_t;
134 static_assert(
sizeof(
void*) == 4);
138 if constexpr (
sizeof(
void*) == 4) {
139 if (__builtin_mul_overflow(
static_cast<LongInt>(value1),
140 static_cast<LongInt>(value2), &prod)) {
144 prod =
static_cast<LongInt>(value1) * value2;
146 const auto whole = prod / divisor;
147 const auto rem =
static_cast<int64_t>(prod % divisor);
151 const auto whole64 =
static_cast<int64_t>(whole);
152 const bool extra_odd_quotient = whole64 % 2 != 0;
153 const int64_t rem_divided =
154 Div<RoundPolicy>(rem, divisor, extra_odd_quotient);
155 UASSERT(rem_divided == -1 || rem_divided == 0 || rem_divided == 1);
157 return whole64 + rem_divided;
160constexpr int Sign(int64_t value) {
return (value > 0) - (value < 0); }
163constexpr int64_t Abs(int64_t value) {
165 return value >= 0 ? value : -value;
171constexpr int64_t Abs(T value) {
172 static_assert(std::is_floating_point_v<T>);
173 return value >= 0 ? value : -value;
179constexpr int64_t Floor(T value) {
180 if (
static_cast<int64_t>(value) <= value) {
181 return static_cast<int64_t>(value);
183 return static_cast<int64_t>(value) - 1;
190constexpr int64_t Ceil(T value) {
191 if (
static_cast<int64_t>(value) >= value) {
192 return static_cast<int64_t>(value);
194 return static_cast<int64_t>(value) + 1;
198template <
typename Int>
199constexpr int64_t ToInt64(Int value) {
200 static_assert(meta::kIsInteger<Int>);
201 static_assert(
sizeof(Int) <=
sizeof(int64_t));
203 if constexpr (
sizeof(Int) ==
sizeof(int64_t)) {
206 return static_cast<int64_t>(value);
209class HalfUpPolicy
final {
212 template <
typename T>
213 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
214 const T abs_remainder = abs -
static_cast<int64_t>(abs);
215 return abs_remainder >= 0.5;
219 static constexpr bool ShouldRoundAwayFromZeroDiv(
220 int64_t a, int64_t b,
bool ) {
221 const int64_t abs_a = impl::Abs(a);
222 const int64_t abs_b = impl::Abs(b);
223 const int64_t half_b = abs_b / 2;
224 const int64_t abs_remainder = abs_a % abs_b;
225 return abs_b % 2 == 0 ? abs_remainder >= half_b : abs_remainder > half_b;
229class HalfDownPolicy
final {
232 template <
typename T>
233 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
234 const T abs_remainder = abs -
static_cast<int64_t>(abs);
235 return abs_remainder > 0.5;
239 static constexpr bool ShouldRoundAwayFromZeroDiv(
240 int64_t a, int64_t b,
bool ) {
241 const int64_t abs_a = impl::Abs(a);
242 const int64_t abs_b = impl::Abs(b);
243 const int64_t half_b = abs_b / 2;
244 const int64_t abs_remainder = abs_a % abs_b;
245 return abs_remainder > half_b;
249class HalfEvenPolicy
final {
252 template <
typename T>
253 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
254 const T abs_remainder = abs -
static_cast<int64_t>(abs);
255 return abs_remainder == 0.5 ? impl::Floor(abs) % 2 != 0
256 : abs_remainder > 0.5;
260 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
261 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)
267 ? ((abs_a / abs_b) % 2 == 0) == extra_odd_quotient
268 : abs_remainder > half_b;
272template <
typename HalfPolicy>
273class HalfRoundPolicyBase {
275 template <
typename T>
276 [[nodiscard]]
static constexpr int64_t Round(T value) {
277 if ((value >= 0.0) == HalfPolicy::ShouldRoundAwayFromZero(value)) {
278 return impl::Ceil(value);
280 return impl::Floor(value);
284 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
285 bool extra_odd_quotient) {
286 if (HalfPolicy::ShouldRoundAwayFromZeroDiv(a, b, extra_odd_quotient)) {
287 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
288 return (a / b) + quotient_sign;
299 if (exp < 0 || exp > impl::kMaxDecimalDigits) {
300 throw std::runtime_error(
"Pow10: invalid power of 10");
302 return impl::kPowSeries10[
static_cast<size_t>(exp)];
313class DefRoundPolicy
final {
315 template <
typename T>
316 [[nodiscard]]
static constexpr int64_t Round(T value) {
317 return static_cast<int64_t>(value + (value < 0 ? -0.5 : 0.5));
320 [[nodiscard]]
static constexpr int64_t DivRounded(
321 int64_t a, int64_t b,
bool ) {
322 const int64_t divisor_corr = impl::Abs(b / 2);
325 return (a + divisor_corr) / b;
328 return (a - divisor_corr) / b;
334class HalfDownRoundPolicy
final
335 :
public impl::HalfRoundPolicyBase<impl::HalfDownPolicy> {};
338class HalfUpRoundPolicy
final
339 :
public impl::HalfRoundPolicyBase<impl::HalfUpPolicy> {};
342class HalfEvenRoundPolicy
final
343 :
public impl::HalfRoundPolicyBase<impl::HalfEvenPolicy> {};
348 template <
typename T>
349 [[nodiscard]]
static constexpr int64_t Round(T value) {
350 return impl::Ceil(value);
353 [[
nodiscard]]
static constexpr int64_t DivRounded(
354 int64_t a, int64_t b,
bool ) {
355 const bool quotient_positive = (a >= 0) == (b >= 0);
356 return (a / b) + (a % b != 0 && quotient_positive);
361class FloorRoundPolicy
final {
363 template <
typename T>
364 [[nodiscard]]
static constexpr int64_t Round(T value) {
365 return impl::Floor(value);
368 [[nodiscard]]
static constexpr int64_t DivRounded(
369 int64_t a, int64_t b,
bool ) {
370 const bool quotient_negative = (a < 0) != (b < 0);
371 return (a / b) - (a % b != 0 && quotient_negative);
376class RoundDownRoundPolicy
final {
378 template <
typename T>
379 [[nodiscard]]
static constexpr int64_t Round(T value) {
380 return static_cast<int64_t>(value);
383 [[nodiscard]]
static constexpr int64_t DivRounded(
384 int64_t a, int64_t b,
bool ) {
390class RoundUpRoundPolicy
final {
392 template <
typename T>
393 [[nodiscard]]
static constexpr int64_t Round(T value) {
395 return impl::Ceil(value);
397 return impl::Floor(value);
401 [[nodiscard]]
static constexpr int64_t DivRounded(
402 int64_t a, int64_t b,
bool ) {
403 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
404 return (a / b) + (a % b != 0) * quotient_sign;
445template <
int Prec,
typename RoundPolicy_ = DefRoundPolicy>
452 using RoundPolicy = RoundPolicy_;
461 template <
typename Int, impl::EnableIfInt<Int> = 0>
477 explicit constexpr Decimal(std::string_view value);
486 template <
typename T>
488 static_assert(std::is_floating_point_v<T>);
489 const auto unbiased_float =
491 if (unbiased_float < impl::kMinInt64 + 1 ||
492 unbiased_float > impl::kMaxInt64 - 1) {
517 result.value_ = value;
533 int original_precision) {
534 const int exponent_for_pack = Prec - original_precision;
536 if (exponent_for_pack >= 0) {
537 return FromUnbiased(original_unbiased) * Pow10(exponent_for_pack);
540 impl::Div<RoundPolicy>(original_unbiased, Pow10(-exponent_for_pack)));
550 *
this = FromDecimal(rhs);
555 template <
typename Int, impl::EnableIfInt<Int> = 0>
561 constexpr bool operator==(
Decimal rhs)
const {
return value_ == rhs.value_; }
563 constexpr bool operator!=(
Decimal rhs)
const {
return value_ != rhs.value_; }
565 constexpr bool operator<(
Decimal rhs)
const {
return value_ < rhs.value_; }
567 constexpr bool operator<=(
Decimal rhs)
const {
return value_ <= rhs.value_; }
569 constexpr bool operator>(
Decimal rhs)
const {
return value_ > rhs.value_; }
571 constexpr bool operator>=(
Decimal rhs)
const {
return value_ >= rhs.value_; }
573 constexpr Decimal operator+()
const {
return *
this; }
575 constexpr Decimal operator-()
const {
581 constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs)
const {
582 if constexpr (Prec2 > Prec) {
583 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
584 }
else if constexpr (Prec2 < Prec) {
585 return *
this + FromDecimal(rhs);
588 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
595 template <
typename Int, impl::EnableIfInt<Int> = 0>
596 constexpr Decimal operator+(Int rhs)
const {
600 template <
typename Int, impl::EnableIfInt<Int> = 0>
607 static_assert(Prec2 <= Prec,
608 "Implicit cast to Decimal of lower precision in assignment");
613 template <
typename Int, impl::EnableIfInt<Int> = 0>
614 constexpr Decimal& operator+=(Int rhs) {
620 constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs)
const {
621 if constexpr (Prec2 > Prec) {
622 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
623 }
else if constexpr (Prec2 < Prec) {
624 return *
this - FromDecimal(rhs);
627 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
634 template <
typename Int, impl::EnableIfInt<Int> = 0>
635 constexpr Decimal operator-(Int rhs)
const {
639 template <
typename Int, impl::EnableIfInt<Int> = 0>
646 static_assert(Prec2 <= Prec,
647 "Implicit cast to Decimal of lower precision in assignment");
652 template <
typename Int, impl::EnableIfInt<Int> = 0>
653 constexpr Decimal& operator-=(Int rhs) {
658 template <
typename Int,
typename = impl::EnableIfInt<Int>>
659 constexpr Decimal operator*(Int rhs)
const {
661 if (rhs > impl::kMaxInt64 ||
662 __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
668 template <
typename Int, impl::EnableIfInt<Int> = 0>
673 template <
typename Int, impl::EnableIfInt<Int> = 0>
674 constexpr Decimal& operator*=(Int rhs) {
680 constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs)
const {
691 template <
typename Int,
typename = impl::EnableIfInt<Int>>
692 constexpr Decimal operator/(Int rhs)
const {
696 template <
typename Int,
typename = impl::EnableIfInt<Int>>
701 template <
typename Int,
typename = impl::EnableIfInt<Int>>
702 constexpr Decimal& operator/=(Int rhs) {
708 constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs)
const {
720 constexpr int Sign()
const {
return impl::Sign(value_); }
728 return *
this / base.AsUnbiased() * base.AsUnbiased();
745 constexpr std::int64_t kLossLimit =
746 (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
748 if (std::abs(value_) < kLossLimit) {
752 constexpr int kCoef =
753 1 << (std::max(std::numeric_limits<std::int64_t>::digits -
754 std::numeric_limits<
double>::digits - 3 * Prec,
775 template <
int Prec2,
typename RoundPolicy2>
776 static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
777 if constexpr (Prec > Prec2) {
779 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>,
784 }
else if constexpr (Prec < Prec2) {
786 impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
792 template <
int Prec2,
typename RoundPolicy2>
795 template <
typename T,
int OldPrec,
typename OldRound>
804struct IsDecimal : std::false_type {};
806template <
int Prec,
typename RoundPolicy>
807struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
828template <
typename T,
int OldPrec,
typename OldRound>
830 static_assert(kIsDecimal<T>);
831 return T::FromDecimal(arg);
839template <
int Prec,
typename RoundPolicy>
840constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before,
842 using Dec =
Decimal<Prec, RoundPolicy>;
843 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
846 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
847 __builtin_add_overflow(result, after, &result)) {
851 return Dec::FromUnbiased(result);
855template <
int Prec,
typename RoundPolicy>
856constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
857 int original_precision) {
858 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
859 UASSERT(after > -Pow10(original_precision) &&
860 after < Pow10(original_precision));
862 if (original_precision <= Prec) {
864 const int missing_digits = Prec - original_precision;
865 const int64_t factor = Pow10(missing_digits);
866 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
869 const int extra_digits = original_precision - Prec;
870 const int64_t factor = Pow10(extra_digits);
873 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
874 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
878struct UnpackedDecimal {
886template <
int Prec,
typename RoundPolicy>
887constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
888 using Dec =
Decimal<Prec, RoundPolicy>;
889 return {dec.AsUnbiased() / Dec::kDecimalFactor,
890 dec.AsUnbiased() % Dec::kDecimalFactor};
896template <
int Prec,
typename RoundPolicy>
897UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
898 if (new_prec == Prec) {
899 return AsUnpacked(dec);
902 if (new_prec > Prec) {
903 if (__builtin_mul_overflow(dec.AsUnbiased(), Pow10(new_prec - Prec),
908 result = impl::Div<RoundPolicy>(dec.AsUnbiased(), Pow10(Prec - new_prec));
910 const auto dec_factor = Pow10(new_prec);
911 return {result / dec_factor, result % dec_factor};
914template <
typename CharT>
915constexpr bool IsSpace(CharT c) {
916 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
919template <
typename CharT,
typename Traits>
920class StringCharSequence {
922 explicit constexpr StringCharSequence(
923 std::basic_string_view<CharT, Traits> sv)
924 : current_(sv.begin()), end_(sv.end()) {}
927 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
929 constexpr void Unget() { --current_; }
932 typename std::basic_string_view<CharT, Traits>::iterator current_;
933 typename std::basic_string_view<CharT, Traits>::iterator end_;
936template <
typename CharT,
typename Traits>
937class StreamCharSequence {
939 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in)
944 constexpr CharT kEof =
945 std::basic_istream<CharT, Traits>::traits_type::eof();
949 const CharT c = in_->peek();
957 void Unget() { in_->unget(); }
960 std::basic_istream<CharT, Traits>* in_;
963enum class ParseOptions {
968 kAllowSpaces = 1 << 0,
972 kAllowTrailingJunk = 1 << 1,
976 kAllowBoundaryDot = 1 << 2,
980 kAllowRounding = 1 << 3
983enum class ParseErrorCode : uint8_t {
1007struct ParseUnpackedResult {
1010 uint8_t decimal_digits{0};
1011 bool is_negative{
false};
1012 std::optional<ParseErrorCode> error;
1013 uint32_t error_position{-1U};
1016enum class ParseState {
1040template <
typename CharSequence>
1041[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(
1042 CharSequence input,
utils::Flags<ParseOptions> options) {
1043 constexpr char dec_point =
'.';
1047 bool is_negative =
false;
1049 ptrdiff_t position = -1;
1050 auto state = ParseState::kSign;
1051 std::optional<ParseErrorCode> error;
1052 int before_digit_count = 0;
1053 uint8_t after_digit_count = 0;
1055 while (state != ParseState::kEnd) {
1056 const auto c = input.Get();
1057 if (c ==
'\0')
break;
1058 if (!error) ++position;
1061 case ParseState::kSign:
1064 state = ParseState::kBeforeFirstDig;
1065 }
else if (c ==
'+') {
1066 state = ParseState::kBeforeFirstDig;
1067 }
else if (c ==
'0') {
1068 state = ParseState::kLeadingZeros;
1069 before_digit_count = 1;
1070 }
else if ((c >=
'1') && (c <=
'9')) {
1071 state = ParseState::kBeforeDec;
1072 before =
static_cast<
int>(c -
'0');
1073 before_digit_count = 1;
1074 }
else if (c == dec_point) {
1075 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1076 error = ParseErrorCode::kBoundaryDot;
1078 state = ParseState::kAfterDec;
1079 }
else if (IsSpace(c)) {
1080 if (!(options & ParseOptions::kAllowSpaces)) {
1081 state = ParseState::kEnd;
1082 error = ParseErrorCode::kSpace;
1085 state = ParseState::kEnd;
1086 error = ParseErrorCode::kWrongChar;
1089 case ParseState::kBeforeFirstDig:
1091 state = ParseState::kLeadingZeros;
1092 before_digit_count = 1;
1093 }
else if ((c >=
'1') && (c <=
'9')) {
1094 state = ParseState::kBeforeDec;
1095 before =
static_cast<
int>(c -
'0');
1096 before_digit_count = 1;
1097 }
else if (c == dec_point) {
1098 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1099 error = ParseErrorCode::kBoundaryDot;
1101 state = ParseState::kAfterDec;
1103 state = ParseState::kEnd;
1104 error = ParseErrorCode::kWrongChar;
1107 case ParseState::kLeadingZeros:
1110 }
else if ((c >=
'1') && (c <=
'9')) {
1111 state = ParseState::kBeforeDec;
1112 before =
static_cast<
int>(c -
'0');
1113 }
else if (c == dec_point) {
1114 state = ParseState::kAfterDec;
1116 state = ParseState::kEnd;
1119 case ParseState::kBeforeDec:
1120 if ((c >=
'0') && (c <=
'9')) {
1121 if (before_digit_count < kMaxDecimalDigits) {
1122 before = 10 * before +
static_cast<
int>(c -
'0');
1123 before_digit_count++;
1124 }
else if (!error) {
1125 error = ParseErrorCode::kOverflow;
1127 }
else if (c == dec_point) {
1128 state = ParseState::kAfterDec;
1130 state = ParseState::kEnd;
1133 case ParseState::kAfterDec:
1134 if ((c >=
'0') && (c <=
'9')) {
1135 if (after_digit_count < kMaxDecimalDigits) {
1136 after = 10 * after +
static_cast<
int>(c -
'0');
1137 after_digit_count++;
1139 if (!(options & ParseOptions::kAllowRounding) && !error) {
1140 error = ParseErrorCode::kRounding;
1142 state = ParseState::kIgnoringAfterDec;
1149 if (!(options & ParseOptions::kAllowBoundaryDot) &&
1150 after_digit_count == 0 && !error) {
1151 error = ParseErrorCode::kBoundaryDot;
1153 state = ParseState::kEnd;
1156 case ParseState::kIgnoringAfterDec:
1157 if ((c >=
'0') && (c <=
'9')) {
1160 state = ParseState::kEnd;
1163 case ParseState::kEnd:
1169 if (state == ParseState::kEnd) {
1172 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1173 if (!(options & ParseOptions::kAllowSpaces)) {
1174 error = ParseErrorCode::kSpace;
1179 const auto c = input.Get();
1180 if (c ==
'\0')
break;
1183 error = ParseErrorCode::kTrailingJunk;
1191 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1192 error = ParseErrorCode::kNoDigits;
1195 if (!error && state == ParseState::kAfterDec &&
1196 !(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0) {
1197 error = ParseErrorCode::kBoundaryDot;
1200 return {before, after, after_digit_count,
1201 is_negative, error,
static_cast<uint32_t>(position)};
1204template <
int Prec,
typename RoundPolicy>
1206 Decimal<Prec, RoundPolicy> decimal;
1207 std::optional<ParseErrorCode> error;
1208 uint32_t error_position{-1U};
1212template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1213[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(
1214 CharSequence input,
utils::Flags<ParseOptions> options) {
1215 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1218 return {{}, parsed.error, parsed.error_position};
1221 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1222 return {{}, ParseErrorCode::kOverflow, 0};
1225 if (!(options & ParseOptions::kAllowRounding) &&
1226 parsed.decimal_digits > Prec) {
1227 return {{}, ParseErrorCode::kRounding, 0};
1230 if (parsed.is_negative) {
1231 parsed.before = -parsed.before;
1232 parsed.after = -parsed.after;
1235 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after,
1236 parsed.decimal_digits),
1241std::string GetErrorMessage(std::string_view source, std::string_view path,
1242 size_t position, ParseErrorCode reason);
1246void TrimTrailingZeros(int64_t& after,
int& after_precision);
1248std::string ToString(int64_t before, int64_t after,
int precision,
1253template <
int Prec,
typename RoundPolicy>
1255 const auto result = impl::Parse<Prec, RoundPolicy>(
1256 impl::StringCharSequence(value), impl::ParseOptions::kNone);
1259 throw ParseError(impl::GetErrorMessage(
1260 value,
"<string>", result.error_position, *result.error));
1262 *
this = result.decimal;
1265template <
int Prec,
typename RoundPolicy>
1266constexpr Decimal<Prec, RoundPolicy>
1267Decimal<Prec, RoundPolicy>::FromStringPermissive(std::string_view input) {
1268 const auto result = impl::Parse<Prec, RoundPolicy>(
1269 impl::StringCharSequence(input),
1270 {impl::ParseOptions::kAllowSpaces, impl::ParseOptions::kAllowBoundaryDot,
1271 impl::ParseOptions::kAllowRounding});
1274 throw ParseError(impl::GetErrorMessage(
1275 input,
"<string>", result.error_position, *result.error));
1277 return result.decimal;
1288template <
int Prec,
typename RoundPolicy>
1290 return fmt::to_string(dec);
1304template <
int Prec,
typename RoundPolicy>
1307 auto precision = format_options.precision.value_or(Prec);
1309 precision = std::min(precision, Prec);
1311 auto [before, after] = impl::AsUnpacked(dec, precision);
1312 return impl::ToString(before, after, precision, format_options);
1323template <
int Prec,
typename RoundPolicy>
1325 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1336template <
int NewPrec,
int Prec,
typename RoundPolicy>
1338 return ToStringTrailingZeros(
1356template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1358 std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1359 if (is.flags() & std::ios_base::skipws) {
1362 const auto result = impl::Parse<Prec, RoundPolicy>(
1363 impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk});
1366 is.setstate(std::ios_base::failbit);
1375template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1376std::basic_ostream<CharT, Traits>& operator<<(
1377 std::basic_ostream<CharT, Traits>& os,
1378 const Decimal<Prec, RoundPolicy>& d) {
1385template <
int Prec,
typename RoundPolicy>
1387 const Decimal<Prec, RoundPolicy>& d) {
1394template <
int Prec,
typename RoundPolicy,
typename Value>
1398 const std::string input = value.
template As<std::string>();
1400 const auto result = impl::Parse<Prec, RoundPolicy>(
1401 impl::StringCharSequence(std::string_view{input}),
1402 impl::ParseOptions::kNone);
1405 throw ParseError(impl::GetErrorMessage(
1406 input, value.GetPath(), result.error_position, *result.error));
1408 return result.decimal;
1413template <
int Prec,
typename RoundPolicy,
typename TargetType>
1416 return typename TargetType::Builder(ToString(object)).ExtractValue();
1421template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1423 StringBuilder& sw) {
1424 WriteToStream(ToString(object), sw);
1429USERVER_NAMESPACE_END
1432template <
int Prec,
typename RoundPolicy>
1436 std::size_t operator()(
const Decimal& v)
const noexcept {
1437 return std::hash<int64_t>{}(v.AsUnbiased());
1449template <
int Prec,
typename RoundPolicy,
typename Char>
1450class fmt::formatter<USERVER_NAMESPACE::decimal64::Decimal<Prec, RoundPolicy>,
1453 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1454 const auto* it = ctx.begin();
1455 const auto* end = ctx.end();
1457 if (it != end && *it ==
'.') {
1458 remove_trailing_zeros_ =
false;
1459 custom_precision_ = 0;
1461 while (it != end && *it >=
'0' && *it <=
'9') {
1462 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1467 if (!custom_precision_ && it != end && *it ==
'f') {
1468 remove_trailing_zeros_ =
false;
1472 if (it != end && *it !=
'}') {
1473 throw format_error(
"invalid format");
1479 template <
typename FormatContext>
1482 FormatContext& ctx)
const {
1483 int after_digits = custom_precision_.value_or(Prec);
1484 auto [before, after] =
1485 USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1486 if (remove_trailing_zeros_) {
1487 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after,
1491 if (after_digits > 0) {
1492 if (dec.Sign() == -1) {
1493 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before,
1494 -after, after_digits);
1496 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before,
1497 after, after_digits);
1500 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1505 bool remove_trailing_zeros_ =
true;
1506 std::optional<
int> custom_precision_;