diff --git a/src/library/yql_common/decimal/yql_decimal.cpp b/src/library/yql_common/decimal/yql_decimal.cpp index ff6474386a..34360e60d3 100644 --- a/src/library/yql_common/decimal/yql_decimal.cpp +++ b/src/library/yql_common/decimal/yql_decimal.cpp @@ -139,7 +139,7 @@ TInt128 FromString(const std::string_view& str, ui8 precision, ui8 scale) { if (IsInf(s)) return neg ? -Inf() : Inf(); if (IsNan(s)) - return neg ? -Nan() : Nan(); + return Nan(); } TUint128 v = 0U; diff --git a/src/library/yql_common/decimal/yql_decimal.h b/src/library/yql_common/decimal/yql_decimal.h index 5272042a15..eee22f9038 100644 --- a/src/library/yql_common/decimal/yql_decimal.h +++ b/src/library/yql_common/decimal/yql_decimal.h @@ -156,5 +156,187 @@ TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c); // a*b/c Only for non zero normal positive multiplier. TInt128 MulAndDivNormalMultiplier(TInt128 a, TInt128 b, TInt128 c); +struct TDecimal { + TInt128 Value = 0; + + TDecimal() = default; + + template + TDecimal(T t): Value(t) { } + + explicit operator TInt128() const { + return Value; + } + + TDecimal& operator+=(TDecimal right) { + const auto l = Value; + const auto r = right.Value; + const auto a = l + r; + if (IsNormal(l) && IsNormal(r) && IsNormal(a)) { + Value = a; + } else if (IsNan(l) || IsNan(r) || !a /* inf - inf*/) { + Value = Nan(); + } else { + Value = a > 0 + ? +Inf() + : -Inf(); + } + return *this; + } + + TDecimal& operator*=(TDecimal right) { + Value = Mul(Value, right.Value); + return *this; + } + + TDecimal& operator/=(TDecimal right) { + Value = Div(Value, right.Value); + return *this; + } + + friend TDecimal operator+(TDecimal left, TDecimal right) { + left += right; + return left; + } + + friend TDecimal operator*(TDecimal left, TDecimal right) { + left *= right; + return left; + } + + friend TDecimal operator/(TDecimal left, TDecimal right) { + left /= right; + return left; + } +}; + +template +class TDecimalMultiplicator { +protected: + const TInt128 Bound; + +public: + TDecimalMultiplicator( + ui8 precision, + [[maybe_unused]] ui8 scale = 0) + : Bound(GetDivider(precision)) + { + } + + TInt128 Do(TInt128 left, TRight right) const { + TInt128 mul = Mul(left, right); + + if (mul > -Bound && mul < +Bound) + return mul; + + return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf()); + } +}; + +template<> +class TDecimalMultiplicator { +protected: + const TInt128 Bound; + const TInt128 Divider; + +public: + TDecimalMultiplicator( + ui8 precision, + ui8 scale) + : Bound(GetDivider(precision)) + , Divider(GetDivider(scale)) + { } + + TInt128 Do(TInt128 left, TInt128 right) const { + TInt128 mul = Divider > 1 ? + MulAndDivNormalDivider(left, right, Divider): + Mul(left, right); + + if (mul > -Bound && mul < +Bound) + return mul; + + return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf()); + } +}; + +template +class TDecimalDivisor { +public: + TDecimalDivisor( + [[maybe_unused]] ui8 precision = 0, + [[maybe_unused]] ui8 scale = 0) + { + } + + TInt128 Do(TInt128 left, TRight right) const { + return Div(left, right); + } +}; + +template<> +class TDecimalDivisor { +protected: + const TInt128 Bound; + const TInt128 Divider; + +public: + TDecimalDivisor( + ui8 precision, + ui8 scale) + : Bound(GetDivider(precision)) + , Divider(GetDivider(scale)) + { } + + TInt128 Do(TInt128 left, TInt128 right) const { + TInt128 div = MulAndDivNormalMultiplier(left, Divider, right); + if (div > -Bound && div < +Bound) { + return div; + } + + return IsNan(div) ? Nan() : (div > 0 ? +Inf() : -Inf()); + } +}; + +template +class TDecimalRemainder { +protected: + const TInt128 Bound; + const TInt128 Divider; + +public: + TDecimalRemainder( + ui8 precision, + ui8 scale) + : Bound(NYql::NDecimal::GetDivider(precision - scale)) + , Divider(NYql::NDecimal::GetDivider(scale)) + { } + + TInt128 Do(TInt128 left, TRight right) const { + if constexpr (std::is_signed::value) { + if (right >= +Bound || right <= -Bound) + return left; + } else { + if (right >= Bound) + return left; + } + + return Mod(left, Mul(Divider, right)); + } +}; + +template<> +class TDecimalRemainder { +public: + TDecimalRemainder( + [[maybe_unused]] ui8 precision = 0, + [[maybe_unused]] ui8 scale = 0) + { + } + + TInt128 Do(TInt128 left, TInt128 right) const { + return NYql::NDecimal::Mod(left, right); + } +}; + } }