35#include <fmt/compile.h>
36#include <fmt/format.h>
38#include <userver/compiler/impl/three_way_comparison.hpp>
39#include <userver/decimal64/format_options.hpp>
40#include <userver/formats/common/meta.hpp>
41#include <userver/utils/assert.hpp>
42#include <userver/utils/flags.hpp>
43#include <userver/utils/meta_light.hpp>
45USERVER_NAMESPACE_BEGIN
59 using std::runtime_error::runtime_error;
77 DivisionByZeroError();
82inline constexpr auto kMaxInt64 = std::numeric_limits<int64_t>::max();
83inline constexpr auto kMinInt64 = std::numeric_limits<int64_t>::min();
87inline constexpr auto kMinRepresentableLongDouble =
88 static_cast<
long double>(impl::kMinInt64) * (1 - 2 * std::numeric_limits<
long double>::epsilon());
89inline constexpr auto kMaxRepresentableLongDouble =
90 static_cast<
long double>(impl::kMaxInt64) * (1 - 2 * std::numeric_limits<
long double>::epsilon());
93using EnableIfInt = std::enable_if_t<meta::kIsInteger<T>,
int>;
96using EnableIfFloat = std::enable_if_t<std::is_floating_point_v<T>,
int>;
99constexpr std::array<int64_t, MaxExp + 1> PowSeries(int64_t base) {
101 std::array<int64_t, MaxExp + 1> result{};
102 for (
int i = 0; i < MaxExp; ++i) {
104 if (pow > kMaxInt64 / base) {
109 result[MaxExp] = pow;
113inline constexpr int kMaxDecimalDigits = 18;
114inline constexpr auto kPowSeries10 = PowSeries<kMaxDecimalDigits>(10);
117static_assert(kMaxInt64 / 10 < kPowSeries10[kMaxDecimalDigits]);
119template <
typename RoundPolicy>
120constexpr int64_t Div(int64_t nominator, int64_t denominator,
bool extra_odd_quotient =
false) {
123 if (denominator == -1) {
128 return RoundPolicy::DivRounded(nominator, denominator, extra_odd_quotient);
132template <
typename RoundPolicy>
133constexpr int64_t MulDiv(int64_t value1, int64_t value2, int64_t divisor) {
136#if __x86_64__
|| __ppc64__ || __aarch64__
137 using LongInt = __int128_t;
138 static_assert(
sizeof(
void*) == 8);
140 using LongInt = int64_t;
141 static_assert(
sizeof(
void*) == 4);
145 if constexpr (
sizeof(
void*) == 4) {
146 if (__builtin_mul_overflow(
static_cast<LongInt>(value1),
static_cast<LongInt>(value2), &prod)) {
150 prod =
static_cast<LongInt>(value1) * value2;
152 const auto whole = prod / divisor;
153 const auto rem =
static_cast<int64_t>(prod % divisor);
157 const auto whole64 =
static_cast<int64_t>(whole);
158 const bool extra_odd_quotient = whole64 % 2 != 0;
159 const int64_t rem_divided = Div<RoundPolicy>(rem, divisor, extra_odd_quotient);
160 UASSERT(rem_divided == -1 || rem_divided == 0 || rem_divided == 1);
162 return whole64 + rem_divided;
165constexpr int Sign(int64_t value) {
return (value > 0) - (value < 0); }
168constexpr int64_t Abs(int64_t value) {
170 return value >= 0 ? value : -value;
176constexpr int64_t Abs(T value) {
177 static_assert(std::is_floating_point_v<T>);
178 return value >= 0 ? value : -value;
184constexpr int64_t Floor(T value) {
185 if (
static_cast<int64_t>(value) <= value) {
186 return static_cast<int64_t>(value);
188 return static_cast<int64_t>(value) - 1;
195constexpr int64_t Ceil(T value) {
196 if (
static_cast<int64_t>(value) >= value) {
197 return static_cast<int64_t>(value);
199 return static_cast<int64_t>(value) + 1;
203template <
typename Int>
204constexpr int64_t ToInt64(Int value) {
205 static_assert(meta::kIsInteger<Int>);
206 static_assert(
sizeof(Int) <=
sizeof(int64_t));
208 if constexpr (
sizeof(Int) ==
sizeof(int64_t)) {
211 return static_cast<int64_t>(value);
214class HalfUpPolicy
final {
217 template <
typename T>
218 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
219 const T abs_remainder = abs -
static_cast<int64_t>(abs);
220 return abs_remainder >= 0.5;
224 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool ) {
225 const int64_t abs_a = impl::Abs(a);
226 const int64_t abs_b = impl::Abs(b);
227 const int64_t half_b = abs_b / 2;
228 const int64_t abs_remainder = abs_a % abs_b;
229 return abs_b % 2 == 0 ? abs_remainder >= half_b : abs_remainder > half_b;
233class HalfDownPolicy
final {
236 template <
typename T>
237 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
238 const T abs_remainder = abs -
static_cast<int64_t>(abs);
239 return abs_remainder > 0.5;
243 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool ) {
244 const int64_t abs_a = impl::Abs(a);
245 const int64_t abs_b = impl::Abs(b);
246 const int64_t half_b = abs_b / 2;
247 const int64_t abs_remainder = abs_a % abs_b;
248 return abs_remainder > half_b;
252class HalfEvenPolicy
final {
255 template <
typename T>
256 [[nodiscard]]
static constexpr bool ShouldRoundAwayFromZero(T abs) {
257 const T abs_remainder = abs -
static_cast<int64_t>(abs);
258 return abs_remainder == 0.5 ? impl::Floor(abs) % 2 != 0 : abs_remainder > 0.5;
262 static constexpr bool ShouldRoundAwayFromZeroDiv(int64_t a, int64_t b,
bool extra_odd_quotient) {
263 const int64_t abs_a = impl::Abs(a);
264 const int64_t abs_b = impl::Abs(b);
265 const int64_t half_b = abs_b / 2;
266 const int64_t abs_remainder = abs_a % abs_b;
267 return (abs_b % 2 == 0 && abs_remainder == half_b) ? ((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,
bool extra_odd_quotient) {
285 if (HalfPolicy::ShouldRoundAwayFromZeroDiv(a, b, extra_odd_quotient)) {
286 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
287 return (a / b) + quotient_sign;
298 if (exp < 0 || exp > impl::kMaxDecimalDigits) {
299 throw std::runtime_error(
"Pow10: invalid power of 10");
301 return impl::kPowSeries10[
static_cast<size_t>(exp)];
312class DefRoundPolicy
final {
314 template <
typename T>
315 [[nodiscard]]
static constexpr int64_t Round(T value) {
316 return static_cast<int64_t>(value + (value < 0 ? -0.5 : 0.5));
319 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
320 const int64_t divisor_corr = impl::Abs(b / 2);
323 return (a + divisor_corr) / b;
326 return (a - divisor_corr) / b;
332class HalfDownRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfDownPolicy> {};
335class HalfUpRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfUpPolicy> {};
338class HalfEvenRoundPolicy
final :
public impl::HalfRoundPolicyBase<impl::HalfEvenPolicy> {};
343 template <
typename T>
344 [[nodiscard]]
static constexpr int64_t Round(T value) {
345 return impl::Ceil(value);
348 [[
nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
349 const bool quotient_positive = (a >= 0) == (b >= 0);
350 return (a / b) + (a % b != 0 && quotient_positive);
355class FloorRoundPolicy
final {
357 template <
typename T>
358 [[nodiscard]]
static constexpr int64_t Round(T value) {
359 return impl::Floor(value);
362 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
363 const bool quotient_negative = (a < 0) != (b < 0);
364 return (a / b) - (a % b != 0 && quotient_negative);
369class RoundDownRoundPolicy
final {
371 template <
typename T>
372 [[nodiscard]]
static constexpr int64_t Round(T value) {
373 return static_cast<int64_t>(value);
376 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
382class RoundUpRoundPolicy
final {
384 template <
typename T>
385 [[nodiscard]]
static constexpr int64_t Round(T value) {
387 return impl::Ceil(value);
389 return impl::Floor(value);
393 [[nodiscard]]
static constexpr int64_t DivRounded(int64_t a, int64_t b,
bool ) {
394 const auto quotient_sign = impl::Sign(a) * impl::Sign(b);
395 return (a / b) + (a % b != 0) * quotient_sign;
436template <
int Prec,
typename TRoundPolicy = DefRoundPolicy>
443 using RoundPolicy = TRoundPolicy;
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_); }
718 return *
this / base.AsUnbiased() * base.AsUnbiased();
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
745 const std::int64_t p1 = value_ / (
kDecimalFactor * kCoef) * kCoef;
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);
860template <
int Prec,
typename RoundPolicy>
861constexpr Decimal<Prec, RoundPolicy> FromUnpackedWithExponent(
864 int original_precision,
866 bool is_negative_exponent,
869 UASSERT(((before >= 0) && (after >= 0)) || ((before <= 0) && (after <= 0)));
872 if (before == 0 && after == 0) {
873 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
876 const int effective_exponent = is_negative_exponent ? -exponent : exponent;
878 if (effective_exponent + Prec < -kMaxDecimalDigits) {
879 return Decimal<Prec, RoundPolicy>::FromUnbiased(0);
882 int total_scale = effective_exponent + Prec - original_precision;
884 if (before == 0 && after != 0) {
885 if (!is_negative_exponent) {
886 if (exponent >= leading_zeros) {
887 exponent -= leading_zeros;
888 original_precision -= leading_zeros;
890 original_precision -= exponent;
896 int64_t value = before;
898 if (__builtin_mul_overflow(value,
Pow10(original_precision
), &value) ||
899 __builtin_add_overflow(value, after, &value)) {
903 if (total_scale > 0) {
904 if (total_scale > kMaxDecimalDigits) {
907 if (__builtin_mul_overflow(value,
Pow10(total_scale
), &value)) {
910 }
else if (total_scale < 0) {
911 const int64_t divisor =
Pow10(-total_scale
);
912 value = Div<RoundPolicy>(value, divisor);
915 return Decimal<Prec, RoundPolicy>::FromUnbiased(value);
918struct UnpackedDecimal {
926template <
int Prec,
typename RoundPolicy>
927constexpr UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec) {
928 using Dec =
Decimal<Prec, RoundPolicy>;
929 return {dec.AsUnbiased() / Dec::kDecimalFactor, dec.AsUnbiased() % Dec::kDecimalFactor};
935template <
int Prec,
typename RoundPolicy>
936UnpackedDecimal AsUnpacked(
Decimal<Prec, RoundPolicy> dec,
int new_prec) {
937 if (new_prec == Prec) {
938 return AsUnpacked(dec);
941 if (new_prec > Prec) {
942 if (__builtin_mul_overflow(dec.AsUnbiased(),
Pow10(new_prec - Prec
), &result)) {
946 result = impl::Div<RoundPolicy>(dec.AsUnbiased(),
Pow10(Prec - new_prec
));
948 const auto dec_factor =
Pow10(new_prec
);
949 return {result / dec_factor, result % dec_factor};
952template <
typename CharT>
953constexpr bool IsSpace(CharT c) {
954 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n' || c ==
'\v';
957template <
typename CharT,
typename Traits>
958class StringCharSequence {
960 explicit constexpr StringCharSequence(std::basic_string_view<CharT, Traits> sv)
961 : current_(sv.begin()), end_(sv.end()) {}
964 constexpr CharT Get() {
return current_ == end_ ? CharT{
'\0'} : *current_++; }
966 constexpr void Unget() { --current_; }
969 typename std::basic_string_view<CharT, Traits>::iterator current_;
970 typename std::basic_string_view<CharT, Traits>::iterator end_;
973template <
typename CharT,
typename Traits>
974class StreamCharSequence {
976 explicit StreamCharSequence(std::basic_istream<CharT, Traits>& in) : in_(&in) {}
980 constexpr CharT kEof = std::basic_istream<CharT, Traits>::traits_type::eof();
984 const CharT c = in_->peek();
992 void Unget() { in_->unget(); }
995 std::basic_istream<CharT, Traits>* in_;
998enum class ParseOptions {
1003 kAllowSpaces = 1 << 0,
1007 kAllowTrailingJunk = 1 << 1,
1011 kAllowBoundaryDot = 1 << 2,
1015 kAllowRounding = 1 << 3,
1019 kAllowExponent = 1 << 4
1022enum class ParseErrorCode : uint8_t {
1046 kExponentNotAllowed,
1053struct ParseUnpackedResult {
1056 uint8_t decimal_digits{0};
1057 bool is_negative{
false};
1058 uint8_t exponent{0};
1059 bool is_negative_exponent{
false};
1060 std::optional<ParseErrorCode> error;
1061 uint32_t error_position{-1U};
1062 int zeros_after_dec{0};
1065enum class ParseState {
1100constexpr inline void
1101StateToExpSign(std::optional<ParseErrorCode>& error, ParseState& state,
utils::Flags<ParseOptions>& options) {
1102 if (!error && !(options & ParseOptions::kAllowExponent)) {
1103 error = ParseErrorCode::kExponentNotAllowed;
1105 state = ParseState::kExpSign;
1109template <
typename CharSequence>
1110[[nodiscard]]
constexpr ParseUnpackedResult ParseUnpacked(CharSequence input,
utils::Flags<ParseOptions> options) {
1111 constexpr char dec_point =
'.';
1115 bool is_negative =
false;
1117 ptrdiff_t position = -1;
1118 auto state = ParseState::kSign;
1119 std::optional<ParseErrorCode> error;
1120 int before_digit_count = 0;
1121 uint8_t after_digit_count = 0;
1122 bool is_negative_exp =
false;
1123 uint8_t exponent = 0;
1124 int zeros_after_dec = 0;
1126 while (state != ParseState::kEnd) {
1127 const auto c = input.Get();
1128 if (c ==
'\0')
break;
1129 if (!error) ++position;
1132 case ParseState::kSign:
1135 state = ParseState::kBeforeFirstDig;
1136 }
else if (c ==
'+') {
1137 state = ParseState::kBeforeFirstDig;
1138 }
else if (c ==
'0') {
1139 state = ParseState::kLeadingZeros;
1140 before_digit_count = 1;
1141 }
else if ((c >=
'1') && (c <=
'9')) {
1142 state = ParseState::kBeforeDec;
1143 before =
static_cast<
int>(c -
'0');
1144 before_digit_count = 1;
1145 }
else if (c == dec_point) {
1146 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1147 error = ParseErrorCode::kBoundaryDot;
1149 state = ParseState::kZerosAfterDec;
1150 }
else if (IsSpace(c)) {
1151 if (!(options & ParseOptions::kAllowSpaces)) {
1152 state = ParseState::kEnd;
1153 error = ParseErrorCode::kSpace;
1156 state = ParseState::kEnd;
1157 error = ParseErrorCode::kWrongChar;
1160 case ParseState::kBeforeFirstDig:
1162 state = ParseState::kLeadingZeros;
1163 before_digit_count = 1;
1164 }
else if ((c >=
'1') && (c <=
'9')) {
1165 state = ParseState::kBeforeDec;
1166 before =
static_cast<
int>(c -
'0');
1167 before_digit_count = 1;
1168 }
else if (c == dec_point) {
1169 if (!(options & ParseOptions::kAllowBoundaryDot) && !error) {
1170 error = ParseErrorCode::kBoundaryDot;
1172 state = ParseState::kAfterDec;
1174 state = ParseState::kEnd;
1175 error = ParseErrorCode::kWrongChar;
1178 case ParseState::kLeadingZeros:
1181 }
else if ((c >=
'1') && (c <=
'9')) {
1182 state = ParseState::kBeforeDec;
1183 before =
static_cast<
int>(c -
'0');
1184 }
else if (c == dec_point) {
1185 state = ParseState::kZerosAfterDec;
1186 }
else if (c ==
'e' || c ==
'E') {
1187 StateToExpSign( error, state, options);
1189 state = ParseState::kEnd;
1192 case ParseState::kBeforeDec:
1193 if ((c >=
'0') && (c <=
'9')) {
1194 if (before_digit_count < kMaxDecimalDigits) {
1195 before = 10 * before +
static_cast<
int>(c -
'0');
1196 before_digit_count++;
1197 }
else if (!error) {
1198 error = ParseErrorCode::kOverflow;
1200 }
else if (c == dec_point) {
1201 state = ParseState::kZerosAfterDec;
1202 }
else if (c ==
'e' || c ==
'E') {
1203 StateToExpSign( error, state, options);
1205 state = ParseState::kEnd;
1209 case ParseState::kZerosAfterDec:
1212 after_digit_count++;
1217 case ParseState::kAfterDec:
1218 state = ParseState::kAfterDec;
1219 if ((c >=
'0') && (c <=
'9')) {
1220 if (after_digit_count < kMaxDecimalDigits) {
1221 after = 10 * after +
static_cast<
int>(c -
'0');
1222 after_digit_count++;
1224 if (!(options & ParseOptions::kAllowRounding) && !error) {
1225 error = ParseErrorCode::kRounding;
1227 state = ParseState::kIgnoringAfterDec;
1234 }
else if (c ==
'e' || c ==
'E') {
1235 StateToExpSign( error, state, options);
1237 if (!(options & ParseOptions::kAllowBoundaryDot) && after_digit_count == 0 && !error) {
1238 error = ParseErrorCode::kBoundaryDot;
1240 state = ParseState::kEnd;
1244 case ParseState::kIgnoringAfterDec:
1245 if ((c >=
'0') && (c <=
'9')) {
1247 }
else if (c ==
'e' || c ==
'E') {
1248 StateToExpSign( error, state, options);
1250 state = ParseState::kEnd;
1254 case ParseState::kExpSign:
1256 is_negative_exp =
false;
1257 state = ParseState::kExpFirstDigit;
1258 }
else if (c ==
'-') {
1259 is_negative_exp =
true;
1260 state = ParseState::kExpFirstDigit;
1261 }
else if (c >=
'0' && c <=
'9') {
1262 exponent =
static_cast<
int>(c -
'0');
1263 state = ParseState::kExpDigits;
1266 error = ParseErrorCode::kWrongChar;
1268 state = ParseState::kEnd;
1272 case ParseState::kExpFirstDigit:
1273 if (c >=
'0' && c <=
'9') {
1274 exponent =
static_cast<
int>(c -
'0');
1275 state = ParseState::kExpDigits;
1277 state = ParseState::kEnd;
1278 if (!error) error = ParseErrorCode::kWrongChar;
1282 case ParseState::kExpDigits:
1283 if (c >=
'0' && c <=
'9') {
1284 if ((__builtin_mul_overflow(10, exponent, &exponent) ||
1285 __builtin_add_overflow(
static_cast<
int>(c -
'0'), exponent, &exponent))) {
1286 if (is_negative_exp && !(options & ParseOptions::kAllowRounding) && !error) {
1287 error = ParseErrorCode::kRounding;
1289 if (!is_negative_exp && !error) {
1290 error = ParseErrorCode::kOverflow;
1294 state = ParseState::kEnd;
1298 case ParseState::kEnd:
1304 if (state == ParseState::kEnd) {
1307 if (!error && !(options & ParseOptions::kAllowTrailingJunk)) {
1308 if (!(options & ParseOptions::kAllowSpaces)) {
1309 error = ParseErrorCode::kSpace;
1314 const auto c = input.Get();
1315 if (c ==
'\0')
break;
1318 error = ParseErrorCode::kTrailingJunk;
1326 if (!error && before_digit_count == 0 && after_digit_count == 0) {
1327 error = ParseErrorCode::kNoDigits;
1330 if (!error && state == ParseState::kZerosAfterDec && !(options & ParseOptions::kAllowBoundaryDot) &&
1331 after_digit_count == 0) {
1332 error = ParseErrorCode::kBoundaryDot;
1335 if ((!error && state == ParseState::kExpSign) || (!error && state == ParseState::kExpFirstDigit)) {
1336 error = ParseErrorCode::kNoExponentDigits;
1339 if (zeros_after_dec >= kMaxDecimalDigits) {
1340 after_digit_count = 0;
1342 zeros_after_dec = 0;
1353 static_cast<uint32_t>(position),
1359template <
int Prec,
typename RoundPolicy>
1361 Decimal<Prec, RoundPolicy> decimal;
1362 std::optional<ParseErrorCode> error;
1363 uint32_t error_position{-1U};
1367template <
int Prec,
typename RoundPolicy,
typename CharSequence>
1368[[nodiscard]]
constexpr ParseResult<Prec, RoundPolicy> Parse(CharSequence input,
utils::Flags<ParseOptions> options) {
1369 ParseUnpackedResult parsed = ParseUnpacked(input, options);
1372 return {{}, parsed.error, parsed.error_position};
1375 if (!(options & ParseOptions::kAllowRounding) && parsed.decimal_digits > Prec) {
1376 return {{}, ParseErrorCode::kRounding, 0};
1379 if (parsed.before >= kMaxInt64 / kPow10<Prec>) {
1380 return {{}, ParseErrorCode::kOverflow, 0};
1383 if (parsed.after == 0 && parsed.decimal_digits > 0) {
1385 parsed.decimal_digits = 0;
1386 parsed.zeros_after_dec = 0;
1389 if (parsed.is_negative) {
1390 parsed.before = -parsed.before;
1391 parsed.after = -parsed.after;
1394 if (parsed.exponent != 0) {
1396 FromUnpackedWithExponent<Prec, RoundPolicy>(
1399 parsed.decimal_digits,
1401 parsed.is_negative_exponent,
1402 parsed.zeros_after_dec
1409 return {FromUnpacked<Prec, RoundPolicy>(parsed.before, parsed.after, parsed.decimal_digits), {}, 0};
1412std::string GetErrorMessage(std::string_view source, std::string_view path, size_t position, ParseErrorCode reason);
1416void TrimTrailingZeros(int64_t& after,
int& after_precision);
1418std::string ToString(int64_t before, int64_t after,
int precision,
const FormatOptions& format_options);
1422template <
int Prec,
typename RoundPolicy>
1424 const auto result = impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(value), impl::ParseOptions::kNone);
1427 throw ParseError(impl::GetErrorMessage(value,
"<string>", result.error_position, *result.error));
1429 *
this = result.decimal;
1432template <
int Prec,
typename RoundPolicy>
1434 const auto result = impl::Parse<Prec, RoundPolicy>(
1435 impl::StringCharSequence(input),
1436 {impl::ParseOptions::kAllowSpaces,
1437 impl::ParseOptions::kAllowBoundaryDot,
1438 impl::ParseOptions::kAllowRounding,
1439 impl::ParseOptions::kAllowExponent}
1443 throw ParseError(impl::GetErrorMessage(input,
"<string>", result.error_position, *result.error));
1445 return result.decimal;
1456template <
int Prec,
typename RoundPolicy>
1458 return fmt::to_string(dec);
1472template <
int Prec,
typename RoundPolicy>
1474 auto precision = format_options
.precision.value_or(Prec);
1476 precision = std::min(precision, Prec);
1478 auto [before, after] = impl::AsUnpacked(dec, precision);
1479 return impl::ToString(before, after, precision, format_options);
1490template <
int Prec,
typename RoundPolicy>
1492 return fmt::format(FMT_COMPILE(
"{:f}"), dec);
1503template <
int NewPrec,
int Prec,
typename RoundPolicy>
1505 return ToStringTrailingZeros(
decimal64::decimal_cast<
Decimal<NewPrec, RoundPolicy>>(dec));
1522template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1523std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& is,
Decimal<Prec, RoundPolicy>& d) {
1524 if (is.flags() & std::ios_base::skipws) {
1527 const auto result = impl::Parse<Prec, RoundPolicy>(
1528 impl::StreamCharSequence(is), {impl::ParseOptions::kAllowTrailingJunk, impl::ParseOptions::kAllowExponent}
1532 is.setstate(std::ios_base::failbit);
1541template <
typename CharT,
typename Traits,
int Prec,
typename RoundPolicy>
1542std::basic_ostream<CharT, Traits>&
1543operator<<(std::basic_ostream<CharT, Traits>& os,
const Decimal<Prec, RoundPolicy>& d) {
1550template <
int Prec,
typename RoundPolicy>
1558template <
int Prec,
typename RoundPolicy,
typename Value>
1561 const std::string input = value.
template As<std::string>();
1564 impl::Parse<Prec, RoundPolicy>(impl::StringCharSequence(std::string_view{input}), impl::ParseOptions::kNone);
1567 throw ParseError(impl::GetErrorMessage(input, value.GetPath(), result.error_position, *result.error));
1569 return result.decimal;
1574template <
int Prec,
typename RoundPolicy,
typename TargetType>
1576 return typename TargetType::Builder(ToString(object)).ExtractValue();
1581template <
int Prec,
typename RoundPolicy,
typename StringBuilder>
1583 WriteToStream(ToString(object), sw);
1587template <
int Prec,
typename RoundPolicy>
1594USERVER_NAMESPACE_END
1597template <
int Prec,
typename RoundPolicy>
1601 std::size_t operator()(
const Decimal& v)
const noexcept {
return std::hash<int64_t>{}(v.AsUnbiased()); }
1612template <
int Prec,
typename RoundPolicy,
typename Char>
1615 constexpr auto parse(fmt::basic_format_parse_context<Char>& ctx) {
1616 const auto* it = ctx.begin();
1617 const auto* end = ctx.end();
1619 if (it != end && *it ==
'.') {
1620 remove_trailing_zeros_ =
false;
1621 custom_precision_ = 0;
1623 while (it != end && *it >=
'0' && *it <=
'9') {
1624 *custom_precision_ = *custom_precision_ * 10 + (*it -
'0');
1629 if (!custom_precision_ && it != end && *it ==
'f') {
1630 remove_trailing_zeros_ =
false;
1634 if (it != end && *it !=
'}') {
1635 throw format_error(
"invalid format");
1641 template <
typename FormatContext>
1642 auto format(
const USERVER_NAMESPACE::
decimal64::
Decimal<Prec, RoundPolicy>& dec, FormatContext& ctx)
const {
1643 int after_digits = custom_precision_.value_or(Prec);
1644 auto [before, after] = USERVER_NAMESPACE::
decimal64::impl::AsUnpacked(dec, after_digits);
1645 if (remove_trailing_zeros_) {
1646 USERVER_NAMESPACE::
decimal64::impl::TrimTrailingZeros(after, after_digits);
1649 if (after_digits > 0) {
1650 if (dec.Sign() == -1) {
1651 return fmt::format_to(ctx.out(), FMT_COMPILE(
"-{}.{:0{}}"), -before, -after, after_digits);
1653 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}.{:0{}}"), before, after, after_digits);
1656 return fmt::format_to(ctx.out(), FMT_COMPILE(
"{}"), before);
1661 bool remove_trailing_zeros_ =
true;
1662 std::optional<
int> custom_precision_;