Skip to content

Commit cc3d9d3

Browse files
spuchinvitstn
andauthored
Fixed parsing decimals from string (#11017) (#11188)
Co-authored-by: Vitaly Stoyan <vvvv@ydb.tech>
1 parent e47d635 commit cc3d9d3

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;
@@ -217,8 +222,16 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
217222
if (!len)
218223
return Err();
219224

220-
const auto exp = std::atoi(++ptr);
221-
if (!exp)
225+
++ptr;
226+
if (ptr != s + str.size() && *ptr == '+') {
227+
++ptr;
228+
if (ptr != s + str.size() && *ptr == '-')
229+
return Err();
230+
}
231+
232+
int exp;
233+
auto [finish, ec] = std::from_chars(ptr, s + str.size(), exp);
234+
if (ec != std::errc() || finish != s + str.size())
222235
return Err();
223236

224237
const int p = precision, s = int(scale) + exp;
@@ -231,8 +244,19 @@ TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
231244
return Err();
232245
}
233246

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

0 commit comments

Comments
 (0)