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) *
88 (1 - 2 * std::numeric_limits<
long double>::epsilon());
89inline constexpr auto kMaxRepresentableLongDouble =
90 static_cast<
long double>(impl::kMaxInt64) *
91 (1 - 2 * std::numeric_limits<
long double>::epsilon());
94using EnableIfInt = std::enable_if_t<meta::kIsInteger<T>,
int>;
97using EnableIfFloat = std::enable_if_t<std::is_floating_point_v<T>,
int>;
100constexpr std::array<int64_t, MaxExp + 1> PowSeries(int64_t base) {
102 std::array<int64_t, MaxExp + 1> result{};
103 for (
int i = 0; i < MaxExp; ++i) {
105 if (pow > kMaxInt64 / base) {
110 result[MaxExp] = pow;
114inline constexpr int kMaxDecimalDigits = 18;
115inline constexpr auto kPowSeries10 = PowSeries<kMaxDecimalDigits>(10);
118static_assert(kMaxInt64 / 10 < kPowSeries10[kMaxDecimalDigits]);
120template <
typename RoundPolicy>
121constexpr int64_t Div(int64_t nominator, int64_t denominator,
122 bool extra_odd_quotient =
false) {
125 if (denominator == -1) {
130 return RoundPolicy::DivRounded(nominator, denominator, extra_odd_quotient);
134template <
typename RoundPolicy>
135constexpr int64_t MulDiv(int64_t value1, int64_t value2, int64_t divisor) {
138#if __x86_64__
|| __ppc64__ || __aarch64__
139 using LongInt = __int128_t;
140 static_assert(
sizeof(
void*) == 8);
142 using LongInt = int64_t;
143 static_assert(
sizeof(
void*) == 4);
147 if constexpr (
sizeof(
void*) == 4) {
148 if (__builtin_mul_overflow(
static_cast<LongInt>(value1),
149 static_cast<LongInt>(value2), &prod)) {
153 prod =
static_cast<LongInt>(value1) * value2;
155 const auto whole = prod / divisor;
156 const auto rem =
static_cast<int64_t>(prod % divisor);
160 const auto whole64 =
static_cast<int64_t>(whole);
161 const bool extra_odd_quotient = whole64 % 2 != 0;
162 const int64_t rem_divided =
163 Div<RoundPolicy>(rem, divisor, extra_odd_quotient);
164 UASSERT(rem_divided == -1 || rem_divided == 0 || rem_divided == 1);
166 return whole64 + rem_divided;
169constexpr int Sign(int64_t value) {
return (value > 0) - (value < 0); }
172constexpr int64_t Abs(int64_t value) {
174 return value >= 0 ? value : -value;
180constexpr int64_t Abs(T value) {
181 static_assert(std::is_floating_point_v<T>);
182 return value >= 0 ? value : -value;
188constexpr int64_t Floor(T value) {
189 if (
static_cast<int64_t>(value) <= value) {
190 return static_cast<int64_t>(value);
192 return static_cast<int64_t>(value) - 1;
199constexpr int64_t Ceil(T value) {
200 if (
static_cast<int64_t>(value) >= value) {
201 return static_cast<int64_t>(value);
203 return static_cast<int64_t>(value) + 1;
207template <
typename Int>
208constexpr int64_t ToInt64(Int value) {
209 static_assert(meta::kIsInteger<Int>);
210 static_assert(
sizeof(Int) <=
sizeof(int64_t));
212 if constexpr (
sizeof(Int) ==
sizeof(int64_t)) {
215 return static_cast<int64_t>(value);
218class HalfUpPolicy
final {
221 template <
typename T>
222 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
223 const T abs_remainder = abs -
static_cast<int64_t>(abs);
224 return abs_remainder >= 0.5;
228 static constexpr bool ShouldRoundAwayFromZeroDiv(
229 int64_t a, int64_t b,
bool ) {
230 const int64_t abs_a = impl::Abs(a);
231 const int64_t abs_b = impl::Abs(b);
232 const int64_t half_b = abs_b / 2;
233 const int64_t abs_remainder = abs_a % abs_b;
234 return abs_b % 2 == 0 ? abs_remainder >= half_b : abs_remainder > half_b;
238class HalfDownPolicy
final {
241 template <
typename T>
242 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
243 const T abs_remainder = abs -
static_cast<int64_t>(abs);
244 return abs_remainder > 0.5;
248 static constexpr bool ShouldRoundAwayFromZeroDiv(
249 int64_t a, int64_t b,
bool ) {
250 const int64_t abs_a = impl::Abs(a);
251 const int64_t abs_b = impl::Abs(b);
252 const int64_t half_b = abs_b / 2;
253 const int64_t abs_remainder = abs_a % abs_b;
254 return abs_remainder > half_b;
258class HalfEvenPolicy
final {
261 template <
typename T>
262 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
263 const T abs_remainder = abs -
static_cast<int64_t>(abs);
264 return abs_remainder == 0.5 ? impl::Floor(abs) % 2 != 0
265 : abs_remainder > 0.5;
269 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
270 bool extra_odd_quotient) {
271 const int64_t abs_a = impl::Abs(a);
272 const int64_t abs_b = impl::Abs(b);
273 const int64_t half_b = abs_b / 2;
274 const int64_t abs_remainder = abs_a % abs_b;
275 return (abs_b % 2 == 0 && abs_remainder == half_b)
276 ? ((abs_a / abs_b) % 2 == 0) == extra_odd_quotient
277 : abs_remainder > half_b;
281template <
typename HalfPolicy>
282class HalfRoundPolicyBase {
284 template <
typename T>
285 [[nodiscard]]
static constexpr int64_t Round(T value) {
286 if ((value >= 0.0) == HalfPolicy::ShouldRoundAwayFromZero(value)) {
287 return impl::Ceil(value);
289 return impl::Floor(value);
293 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
294 bool extra_odd_quotient) {
295 if (HalfPolicy::ShouldRoundAwayFromZeroDiv(a, b, extra_odd_quotient)) {
296 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
297 return (a / b) + quotient_sign;
308 if (exp < 0 || exp > impl::kMaxDecimalDigits) {
309 throw std::runtime_error(
"Pow10: invalid power of 10");
311 return impl::kPowSeries10[
static_cast<size_t>(exp)];
322class DefRoundPolicy
final {
324 template <
typename T>
325 [[nodiscard]]
static constexpr int64_t Round(T value) {
326 return static_cast<int64_t>(value + (value < 0 ? -0.5 : 0.5));
329 [[nodiscard]]
static constexpr int64_t DivRounded(
330 int64_t a, int64_t b,
bool ) {
331 const int64_t divisor_corr = impl::Abs(b / 2);
334 return (a + divisor_corr) / b;
337 return (a - divisor_corr) / b;
343class HalfDownRoundPolicy
final
344 :
public impl::HalfRoundPolicyBase<impl::HalfDownPolicy> {};
347class HalfUpRoundPolicy
final
348 :
public impl::HalfRoundPolicyBase<impl::HalfUpPolicy> {};
351class HalfEvenRoundPolicy
final
352 :
public impl::HalfRoundPolicyBase<impl::HalfEvenPolicy> {};
357 template <
typename T>
358 [[nodiscard]]
static constexpr int64_t Round(T value) {
359 return impl::Ceil(value);
362 [[
nodiscard]]
static constexpr int64_t DivRounded(
363 int64_t a, int64_t b,
bool ) {
364 const bool quotient_positive = (a >= 0) == (b >= 0);
365 return (a / b) + (a % b != 0 && quotient_positive);
370class FloorRoundPolicy
final {
372 template <
typename T>
373 [[nodiscard]]
static constexpr int64_t Round(T value) {
374 return impl::Floor(value);
377 [[nodiscard]]
static constexpr int64_t DivRounded(
378 int64_t a, int64_t b,
bool ) {
379 const bool quotient_negative = (a < 0) != (b < 0);
380 return (a / b) - (a % b != 0 && quotient_negative);
385class RoundDownRoundPolicy
final {
387 template <
typename T>
388 [[nodiscard]]
static constexpr int64_t Round(T value) {
389 return static_cast<int64_t>(value);
392 [[nodiscard]]
static constexpr int64_t DivRounded(
393 int64_t a, int64_t b,
bool ) {
399class RoundUpRoundPolicy
final {
401 template <
typename T>
402 [[nodiscard]]
static constexpr int64_t Round(T value) {
404 return impl::Ceil(value);
406 return impl::Floor(value);
410 [[nodiscard]]
static constexpr int64_t DivRounded(
411 int64_t a, int64_t b,
bool ) {
412 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
413 return (a / b) + (a % b != 0) * quotient_sign;
454template <
int Prec,
typename RoundPolicy_ = DefRoundPolicy>
461 using RoundPolicy = RoundPolicy_;
470 template <
typename Int, impl::EnableIfInt<Int> = 0>
486 explicit constexpr Decimal(std::string_view value);
495 template <
typename T>
497 static_assert(std::is_floating_point_v<T>);
500 static_assert(DefRoundPolicy::Round(impl::kMinRepresentableLongDouble) < 0);
501 static_assert(DefRoundPolicy::Round(impl::kMaxRepresentableLongDouble) > 0);
503 const auto unbiased_float =
505 if (unbiased_float < impl::kMinRepresentableLongDouble ||
506 unbiased_float > impl::kMaxRepresentableLongDouble) {
531 result.value_ = value;
547 int original_precision) {
548 const int exponent_for_pack = Prec - original_precision;
550 if (exponent_for_pack >= 0) {
551 return FromUnbiased(original_unbiased) * Pow10(exponent_for_pack);
554 impl::Div<RoundPolicy>(original_unbiased, Pow10(-exponent_for_pack)));
564 *
this = FromDecimal(rhs);
569 template <
typename Int, impl::EnableIfInt<Int> = 0>
575 constexpr bool operator==(
Decimal rhs)
const {
return value_ == rhs.value_; }
577 constexpr bool operator!=(
Decimal rhs)
const {
return value_ != rhs.value_; }
579 constexpr bool operator<(
Decimal rhs)
const {
return value_ < rhs.value_; }
581 constexpr bool operator<=(
Decimal rhs)
const {
return value_ <= rhs.value_; }
583 constexpr bool operator>(
Decimal rhs)
const {
return value_ > rhs.value_; }
585 constexpr bool operator>=(
Decimal rhs)
const {
return value_ >= rhs.value_; }
587 constexpr Decimal operator+()
const {
return *
this; }
589 constexpr Decimal operator-()
const {
595 constexpr auto operator+(
Decimal<Prec2, RoundPolicy> rhs)
const {
596 if constexpr (Prec2 > Prec) {
597 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) + rhs;
598 }
else if constexpr (Prec2 < Prec) {
599 return *
this + FromDecimal(rhs);
602 if (__builtin_add_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
609 template <
typename Int, impl::EnableIfInt<Int> = 0>
610 constexpr Decimal operator+(Int rhs)
const {
614 template <
typename Int, impl::EnableIfInt<Int> = 0>
621 static_assert(Prec2 <= Prec,
622 "Implicit cast to Decimal of lower precision in assignment");
627 template <
typename Int, impl::EnableIfInt<Int> = 0>
628 constexpr Decimal& operator+=(Int rhs) {
634 constexpr auto operator-(
Decimal<Prec2, RoundPolicy> rhs)
const {
635 if constexpr (Prec2 > Prec) {
636 return Decimal<Prec2, RoundPolicy>::FromDecimal(*
this) - rhs;
637 }
else if constexpr (Prec2 < Prec) {
638 return *
this - FromDecimal(rhs);
641 if (__builtin_sub_overflow(
AsUnbiased(), rhs.AsUnbiased(), &result)) {
648 template <
typename Int, impl::EnableIfInt<Int> = 0>
649 constexpr Decimal operator-(Int rhs)
const {
653 template <
typename Int, impl::EnableIfInt<Int> = 0>
660 static_assert(Prec2 <= Prec,
661 "Implicit cast to Decimal of lower precision in assignment");
666 template <
typename Int, impl::EnableIfInt<Int> = 0>
667 constexpr Decimal& operator-=(Int rhs) {
672 template <
typename Int,
typename = impl::EnableIfInt<Int>>
673 constexpr Decimal operator*(Int rhs)
const {
675 if (rhs > impl::kMaxInt64 ||
676 __builtin_mul_overflow(value_,
static_cast<int64_t>(rhs), &result)) {
682 template <
typename Int, impl::EnableIfInt<Int> = 0>
687 template <
typename Int, impl::EnableIfInt<Int> = 0>
688 constexpr Decimal& operator*=(Int rhs) {
694 constexpr Decimal operator*(
Decimal<Prec2, RoundPolicy> rhs)
const {
705 template <
typename Int,
typename = impl::EnableIfInt<Int>>
706 constexpr Decimal operator/(Int rhs)
const {
710 template <
typename Int,
typename = impl::EnableIfInt<Int>>
715 template <
typename Int,
typename = impl::EnableIfInt<Int>>
716 constexpr Decimal& operator/=(Int rhs) {
722 constexpr Decimal operator/(
Decimal<Prec2, RoundPolicy> rhs)
const {
734 constexpr int Sign()
const {
return impl::Sign(value_); }
742 return *
this / base.AsUnbiased() * base.AsUnbiased();
759 constexpr std::int64_t kLossLimit =
760 (
static_cast<std::int64_t>(1) << std::numeric_limits<
double>::digits);
762 if (value_ > -kLossLimit && value_ < kLossLimit) {
766 constexpr int kCoef =
767 1 << (std::max(std::numeric_limits<std::int64_t>::digits -
768 std::numeric_limits<
double>::digits - 3 * Prec,
789 template <
int Prec2,
typename RoundPolicy2>
790 static constexpr Decimal FromDecimal(
Decimal<Prec2, RoundPolicy2> source) {
791 if constexpr (Prec > Prec2) {
793 if (__builtin_mul_overflow(source.AsUnbiased(), kPow10<Prec - Prec2>,
798 }
else if constexpr (Prec < Prec2) {
800 impl::Div<RoundPolicy>(source.AsUnbiased(), kPow10<Prec2 - Prec>)
);
806 template <
int Prec2,
typename RoundPolicy2>
809 template <
typename T,
int OldPrec,
typename OldRound>
818struct IsDecimal : std::false_type {};
820template <
int Prec,
typename RoundPolicy>
821struct IsDecimal<
Decimal<Prec, RoundPolicy>> : std::true_type {};
842template <
typename T,
int OldPrec,
typename OldRound>
844 static_assert(kIsDecimal<T>);
845 return T::FromDecimal(arg);
853template <
int Prec,
typename RoundPolicy>
854constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before,
856 using Dec =
Decimal<Prec, RoundPolicy>;
857 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
860 if (__builtin_mul_overflow(before, Dec::kDecimalFactor, &result) ||
861 __builtin_add_overflow(result, after, &result)) {
865 return Dec::FromUnbiased(result);
869template <
int Prec,
typename RoundPolicy>
870constexpr Decimal<Prec, RoundPolicy> FromUnpacked(int64_t before, int64_t after,
871 int original_precision) {
872 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
873 UASSERT(after > -Pow10(original_precision) &&
874 after < Pow10(original_precision));
876 if (original_precision <= Prec) {
878 const int missing_digits = Prec - original_precision;
879 const int64_t factor = Pow10(missing_digits);
880 return FromUnpacked<Prec, RoundPolicy>(before, after * factor);
883 const int extra_digits = original_precision - Prec;
884 const int64_t factor = Pow10(extra_digits);
887 const int64_t rounded_after = Div<RoundPolicy>(after, factor);
888 return FromUnpacked<Prec, RoundPolicy>(before, rounded_after);
892struct UnpackedDecimal {
900template <
int Prec,
typename RoundPolicy>
901constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
902 using Dec =
Decimal<Prec, RoundPolicy>;
903 return {dec.AsUnbiased() / Dec::kDecimalFactor,
904 dec.AsUnbiased() % Dec::kDecimalFactor};
910template <
int Prec,
typename RoundPolicy>
911UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
912 if (new_prec == Prec) {
913 return AsUnpacked(dec);
916 if (new_prec > Prec) {
917 if (__builtin_mul_overflow(dec.AsUnbiased(), Pow10(new_prec - Prec),
922 result = impl::Div<RoundPolicy>(dec.AsUnbiased(), Pow10(Prec - new_prec));
924 const auto dec_factor = Pow10(new_prec);
925 return {result / dec_factor, result % dec_factor};
928template <
typename CharT>
929constexpr bool IsSpace(CharT c) {
930 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
933template <
typename CharT,
typename Traits>
934class StringCharSequence {
936 explicit constexpr StringCharSequence(
937 std::basic_string_view<CharT, Traits> sv)
938 : current_(sv.begin()), end_(sv.end()) {}
941 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
943 constexpr void Unget() { --current_; }
946 typename std::basic_string_view<CharT, Traits>::iterator current_;
947 typename std::basic_string_view<CharT, Traits>::iterator end_;
950template <
typename CharT,
typename Traits>
951class StreamCharSequence {
953 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in)
958 constexpr CharT kEof =
959 std::basic_istream<CharT, Traits>::traits_type::eof();
963 const CharT c = in_->peek();
971 void Unget() { in_->unget(); }
974 std::basic_istream<CharT, Traits>* in_;
977enum class ParseOptions {
982 kAllowSpaces = 1 << 0,
986 kAllowTrailingJunk = 1 << 1,
990 kAllowBoundaryDot = 1 << 2,
994 kAllowRounding = 1 << 3
997enum class ParseErrorCode : uint8_t {
1021struct ParseUnpackedResult {
1024 uint8_t decimal_digits{0};
1025 bool is_negative{
false};
1026 std::optional<ParseErrorCode> error;
1027 uint32_t error_position{-1U};
1030enum class ParseState {
1054template <
typename CharSequence>
1055[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(
1056 CharSequence input, utils::Flags<ParseOptions> options) {
1057 constexpr char dec_point =
'.';
1061 bool is_negative =
false;
1063 ptrdiff_t position = -1;
1064 auto state = ParseState::kSign;
1065 std::optional<ParseErrorCode> error;
1066 int before_digit_count = 0;
1067 uint8_t after_digit_count = 0;
1069 while (state != ParseState::kEnd) {
1070 const auto c = input.Get();
1071 if (c ==
'\0')
break;
1072 if (!error) ++position;
1075 case ParseState::kSign:
1078 state = ParseState::kBeforeFirstDig;
1079 }
else if (c ==
'+') {
1080 state = ParseState::kBeforeFirstDig;
1081 }
else if (c ==
'0') {
1082 state = ParseState::kLeadingZeros;
1083 before_digit_count = 1;
1084 }
else if ((c >=
'1') && (c <=
'9')) {
1085 state = ParseState::kBeforeDec;
1086 before =
static_cast<
int>(c -
'0');
1087 before_digit_count = 1;
1088 }
else if (c == dec_point) {
1089 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1090 error = ParseErrorCode::kBoundaryDot;
1092 state = ParseState::kAfterDec;
1093 }
else if (IsSpace(c)) {
1094 if (!(options & ParseOptions::kAllowSpaces)) {
1095 state = ParseState::kEnd;
1096 error = ParseErrorCode::kSpace;
1099 state = ParseState::kEnd;
1100 error = ParseErrorCode::kWrongChar;
1103 case ParseState::kBeforeFirstDig:
1105 state = ParseState::kLeadingZeros;
1106 before_digit_count = 1;
1107 }
else if ((c >=
'1') && (c <=
'9')) {
1108 state = ParseState::kBeforeDec;
1109 before =
static_cast<
int>(c -
'0');
1110 before_digit_count = 1;
1111 }
else if (c == dec_point) {
1112 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1113 error = ParseErrorCode::kBoundaryDot;
1115 state = ParseState::kAfterDec;
1117 state = ParseState::kEnd;
1118 error = ParseErrorCode::kWrongChar;
1121 case ParseState::kLeadingZeros:
1124 }
else if ((c >=
'1') && (c <=
'9')) {
1125 state = ParseState::kBeforeDec;
1126 before =
static_cast<
int>(c -
'0');
1127 }
else if (c == dec_point) {
1128 state = ParseState::kAfterDec;
1130 state = ParseState::kEnd;
1133 case ParseState::kBeforeDec:
1134 if ((c >=
'0') && (c <=
'9')) {
1135 if (before_digit_count < kMaxDecimalDigits) {
1136 before = 10 * before +
static_cast<
int>(c -
'0');
1137 before_digit_count++;
1138 }
else if (!error) {
1139 error = ParseErrorCode::kOverflow;
1141 }
else if (c == dec_point) {
1142 state = ParseState::kAfterDec;
1144 state = ParseState::kEnd;
1147 case ParseState::kAfterDec:
1148 if ((c >=
'0') && (c <=
'9')) {
1149 if (after_digit_count < kMaxDecimalDigits) {
1150 after = 10 * after +
static_cast<
int>(c -
'0');
1151 after_digit_count++;
1153 if (!(options & ParseOptions::kAllowRounding) && !error) {
1154 error = ParseErrorCode::kRounding;
1156 state = ParseState::kIgnoringAfterDec;
1163 if (!(options & ParseOptions::kAllowBoundaryDot) &&
1164 after_digit_count == 0 && !error) {
1165 error = ParseErrorCode::kBoundaryDot;
1167 state = ParseState::kEnd;
1170 case ParseState::kIgnoringAfterDec:
1171 if ((c >=
'0') && (c <=
'9')) {
1174 state = ParseState::kEnd;
1177 case ParseState::kEnd:
1183 if (state == ParseState::kEnd) {
1186 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1187 if (!(options & ParseOptions::kAllowSpaces)) {
1188 error = ParseErrorCode::kSpace;
1193 const auto c = input.Get();
1194 if (c ==
'\0')
break;
1197 error = ParseErrorCode::kTrailingJunk;
1205 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1206 error = ParseErrorCode::kNoDigits;
1209 if (!error && state == ParseState::kAfterDec &&
1210 !(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0) {
1211 error = ParseErrorCode::kBoundaryDot;
1214 return {before, after, after_digit_count,
1215 is_negative, error,
static_cast<uint32_t>(position)};
1218template <
int Prec,
typename RoundPolicy>
1220 Decimal<Prec, RoundPolicy> decimal;
1221 std::optional<ParseErrorCode> error;
1222 uint32_t error_position{-1U};
1226template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1227[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(
1228 CharSequence input, utils::Flags<ParseOptions> options) {
1229 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1232 return {{}, parsed.error, parsed.error_position};
1235 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1236 return {{}, ParseErrorCode::kOverflow, 0};
1239 if (!(options & ParseOptions::kAllowRounding) &&
1240 parsed.decimal_digits > Prec) {
1241 return {{}, ParseErrorCode::kRounding, 0};
1244 if (parsed.is_negative) {
1245 parsed.before = -parsed.before;
1246 parsed.after = -parsed.after;
1249 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after,
1250 parsed.decimal_digits),
1255std::string GetErrorMessage(std::string_view source, std::string_view path,
1256 size_t position, ParseErrorCode reason);
1260void TrimTrailingZeros(int64_t& after,
int& after_precision);
1262std::string ToString(int64_t before, int64_t after,
int precision,
1267template <
int Prec,
typename RoundPolicy>
1269 const auto result = impl::Parse<Prec, RoundPolicy>(
1270 impl::StringCharSequence(value), impl::ParseOptions::kNone);
1273 throw ParseError(impl::GetErrorMessage(
1274 value,
"<string>", result.error_position, *result.error));
1276 *
this = result.decimal;
1279template <
int Prec,
typename RoundPolicy>
1280constexpr Decimal<Prec, RoundPolicy>
1281Decimal<Prec, RoundPolicy>::FromStringPermissive(std::string_view input) {
1282 const auto result = impl::Parse<Prec, RoundPolicy>(
1283 impl::StringCharSequence(input),
1284 {impl::ParseOptions::kAllowSpaces, impl::ParseOptions::kAllowBoundaryDot,
1285 impl::ParseOptions::kAllowRounding});
1288 throw ParseError(impl::GetErrorMessage(
1289 input,
"<string>", result.error_position, *result.error));
1291 return result.decimal;
1302template <
int Prec,
typename RoundPolicy>
1304 return fmt::to_string(dec);
1318template <
int Prec,
typename RoundPolicy>
1321 auto precision = format_options.precision.value_or(Prec);
1323 precision = std::min(precision, Prec);
1325 auto [before, after] = impl::AsUnpacked(dec, precision);
1326 return impl::ToString(before, after, precision, format_options);
1337template <
int Prec,
typename RoundPolicy>
1339 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1350template <
int NewPrec,
int Prec,
typename RoundPolicy>
1352 return ToStringTrailingZeros(
1370template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1372 std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1373 if (is.flags() & std::ios_base::skipws) {
1376 const auto result = impl::Parse<Prec, RoundPolicy>(
1377 impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk});
1380 is.setstate(std::ios_base::failbit);
1389template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1390std::basic_ostream<CharT, Traits>& operator<<(
1391 std::basic_ostream<CharT, Traits>& os,
1392 const Decimal<Prec, RoundPolicy>& d) {
1399template <
int Prec,
typename RoundPolicy>
1401 const Decimal<Prec, RoundPolicy>& d) {
1408template <
int Prec,
typename RoundPolicy,
typename Value>
1412 const std::string input = value.
template As<std::string>();
1414 const auto result = impl::Parse<Prec, RoundPolicy>(
1415 impl::StringCharSequence(std::string_view{input}),
1416 impl::ParseOptions::kNone);
1419 throw ParseError(impl::GetErrorMessage(
1420 input, value.GetPath(), result.error_position, *result.error));
1422 return result.decimal;
1427template <
int Prec,
typename RoundPolicy,
typename TargetType>
1430 return typename TargetType::Builder(ToString(object)).ExtractValue();
1435template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1437 StringBuilder& sw) {
1438 WriteToStream(ToString(object), sw);
1443USERVER_NAMESPACE_END
1446template <
int Prec,
typename RoundPolicy>
1450 std::size_t operator()(
const Decimal& v)
const noexcept {
1451 return std::hash<int64_t>{}(v.AsUnbiased());
1463template <
int Prec,
typename RoundPolicy,
typename Char>
1464class fmt::formatter<USERVER_NAMESPACE::decimal64::Decimal<Prec, RoundPolicy>,
1467 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1468 const auto* it = ctx.begin();
1469 const auto* end = ctx.end();
1471 if (it != end && *it ==
'.') {
1472 remove_trailing_zeros_ =
false;
1473 custom_precision_ = 0;
1475 while (it != end && *it >=
'0' && *it <=
'9') {
1476 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1481 if (!custom_precision_ && it != end && *it ==
'f') {
1482 remove_trailing_zeros_ =
false;
1486 if (it != end && *it !=
'}') {
1487 throw format_error(
"invalid format");
1493 template <
typename FormatContext>
1496 FormatContext& ctx)
const {
1497 int after_digits = custom_precision_.value_or(Prec);
1498 auto [before, after] =
1499 USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1500 if (remove_trailing_zeros_) {
1501 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after,
1505 if (after_digits > 0) {
1506 if (dec.Sign() == -1) {
1507 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before,
1508 -after, after_digits);
1510 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before,
1511 after, after_digits);
1514 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1519 bool remove_trailing_zeros_ =
true;
1520 std::optional<
int> custom_precision_;