diff --git a/ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp b/ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp index 9a79bbb53d65..a179faa28ed7 100644 --- a/ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp +++ b/ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp @@ -215,6 +215,8 @@ Y_UNIT_TEST_SUITE(TYqlDecimalTest) { UNIT_ASSERT(FromStringEx("-1e-99", 10, 2) == 0); UNIT_ASSERT(FromStringEx("-510e-3", 1, 0) == -1); UNIT_ASSERT(FromStringEx("+99E3", 5, 0) == 99000); + UNIT_ASSERT(FromStringEx("2.1E-130", 35, 2) == 0); + UNIT_ASSERT(FromStringEx("2.1E0", 35, 2) == 210); } Y_UNIT_TEST(TestFormStringExInvalidValues) { @@ -225,8 +227,11 @@ Y_UNIT_TEST_SUITE(TYqlDecimalTest) { UNIT_ASSERT(IsError(FromStringEx("E2", 35, 15))); // empty UNIT_ASSERT(IsError(FromStringEx("E2E4", 35, 15))); // empty - UNIT_ASSERT(IsError(FromStringEx("12E0", 35, 15))); // zero isn't avail UNIT_ASSERT(IsError(FromStringEx("NANE5", 35, 15))); // nan with exp + UNIT_ASSERT(IsError(FromStringEx("infE5", 35, 15))); // inf with exp + UNIT_ASSERT(IsError(FromStringEx("-infe-5", 35, 15))); // inf with exp + UNIT_ASSERT(IsError(FromStringEx("2.1E0X", 35, 2))); // not fully parsed exp + UNIT_ASSERT(IsError(FromStringEx("2.1E+-1", 35, 2))); // two signs } Y_UNIT_TEST(TestSpecialAsString) { diff --git a/ydb/library/yql/public/decimal/yql_decimal.cpp b/ydb/library/yql/public/decimal/yql_decimal.cpp index d444557c42ee..9d91cf101863 100644 --- a/ydb/library/yql/public/decimal/yql_decimal.cpp +++ b/ydb/library/yql/public/decimal/yql_decimal.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace NYql { namespace NDecimal { @@ -10,6 +11,10 @@ namespace NDecimal { static const TUint128 Ten(10U); TUint128 GetDivider(ui8 scale) { + if (scale > MaxPrecision) { + return Inf(); + } + TUint128 d(1U); while (scale--) d *= Ten; @@ -217,8 +222,16 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) { if (!len) return Err(); - const auto exp = std::atoi(++ptr); - if (!exp) + ++ptr; + if (ptr != s + str.size() && *ptr == '+') { + ++ptr; + if (ptr != s + str.size() && *ptr == '-') + return Err(); + } + + int exp; + auto [finish, ec] = std::from_chars(ptr, s + str.size(), exp); + if (ec != std::errc() || finish != s + str.size()) return Err(); const int p = precision, s = int(scale) + exp; @@ -231,8 +244,19 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) { return Err(); } + if (IsInf(r)) { + auto p = str.data(); + if (*p == '+' || *p == '-') + ++p; + + if (!std::isdigit(*p)) + return Err(); + + return r; + } + if (const auto e = exp > 0 ? std::max(0, s - p) : std::min(0, s)) { - if (r && IsNormal(r)) { + if (r) { if (exp > 0) return Mul(r, GetDivider(+e)); if (exp < 0)