Skip to content

Commit 1dd898c

Browse files
authored
Fixed parsing decimals from string (#11017)
1 parent 012816b commit 1dd898c

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

ydb/library/yql/public/decimal/ut/yql_decimal_ut.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ Y_UNIT_TEST_SUITE(TYqlDecimalTest) {
215215
UNIT_ASSERT(FromStringEx("-1e-99", 10, 2) == 0);
216216
UNIT_ASSERT(FromStringEx("-510e-3", 1, 0) == -1);
217217
UNIT_ASSERT(FromStringEx("+99E3", 5, 0) == 99000);
218+
UNIT_ASSERT(FromStringEx("2.1E-130", 35, 2) == 0);
219+
UNIT_ASSERT(FromStringEx("2.1E0", 35, 2) == 210);
218220
}
219221

220222
Y_UNIT_TEST(TestFormStringExInvalidValues) {
@@ -225,8 +227,11 @@ Y_UNIT_TEST_SUITE(TYqlDecimalTest) {
225227

226228
UNIT_ASSERT(IsError(FromStringEx("E2", 35, 15))); // empty
227229
UNIT_ASSERT(IsError(FromStringEx("E2E4", 35, 15))); // empty
228-
UNIT_ASSERT(IsError(FromStringEx("12E0", 35, 15))); // zero isn't avail
229230
UNIT_ASSERT(IsError(FromStringEx("NANE5", 35, 15))); // nan with exp
231+
UNIT_ASSERT(IsError(FromStringEx("infE5", 35, 15))); // inf with exp
232+
UNIT_ASSERT(IsError(FromStringEx("-infe-5", 35, 15))); // inf with exp
233+
UNIT_ASSERT(IsError(FromStringEx("2.1E0X", 35, 2))); // not fully parsed exp
234+
UNIT_ASSERT(IsError(FromStringEx("2.1E+-1", 35, 2))); // two signs
230235
}
231236

232237
Y_UNIT_TEST(TestSpecialAsString) {

ydb/library/yql/public/decimal/yql_decimal.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
#include <cstring>
44
#include <ostream>
55
#include <string>
6+
#include <charconv>
67

78
namespace NYql {
89
namespace NDecimal {
910

1011
static const TUint128 Ten(10U);
1112

1213
TUint128 GetDivider(ui8 scale) {
14+
if (scale > MaxPrecision) {
15+
return Inf();
16+
}
17+
1318
TUint128 d(1U);
1419
while (scale--)
1520
d *= Ten;
@@ -215,8 +220,16 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
215220
if (!len)
216221
return Err();
217222

218-
const auto exp = std::atoi(++ptr);
219-
if (!exp)
223+
++ptr;
224+
if (ptr != s + str.size() && *ptr == '+') {
225+
++ptr;
226+
if (ptr != s + str.size() && *ptr == '-')
227+
return Err();
228+
}
229+
230+
int exp;
231+
auto [finish, ec] = std::from_chars(ptr, s + str.size(), exp);
232+
if (ec != std::errc() || finish != s + str.size())
220233
return Err();
221234

222235
const int p = precision, s = int(scale) + exp;
@@ -229,8 +242,19 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
229242
return Err();
230243
}
231244

245+
if (IsInf(r)) {
246+
auto p = str.data();
247+
if (*p == '+' || *p == '-')
248+
++p;
249+
250+
if (!std::isdigit(*p))
251+
return Err();
252+
253+
return r;
254+
}
255+
232256
if (const auto e = exp > 0 ? std::max(0, s - p) : std::min(0, s)) {
233-
if (r && IsNormal(r)) {
257+
if (r) {
234258
if (exp > 0)
235259
return Mul(r, GetDivider(+e));
236260
if (exp < 0)

0 commit comments

Comments
 (0)