Skip to content

Commit

Permalink
Fixed parsing decimals from string (ydb-platform#11017)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitstn committed Oct 29, 2024
1 parent 067b582 commit 6629a3f
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 4 deletions.
7 changes: 6 additions & 1 deletion ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
30 changes: 27 additions & 3 deletions ydb/library/yql/public/decimal/yql_decimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
#include <cstring>
#include <ostream>
#include <string>
#include <charconv>

namespace NYql {
namespace NDecimal {

static const TUint128 Ten(10U);

TUint128 GetDivider(ui8 scale) {
if (scale > MaxPrecision) {
return Inf();
}

TUint128 d(1U);
while (scale--)
d *= Ten;
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down

0 comments on commit 6629a3f

Please sign in to comment.