Skip to content

Commit 76cf4c2

Browse files
authored
Merge pull request #232 from scratchcpp/fix_comparison_of_number_strings
Fix #195: Fix string to double conversion (space before number)
2 parents 9e4e6fd + 718e67b commit 76cf4c2

File tree

2 files changed

+133
-39
lines changed

2 files changed

+133
-39
lines changed

include/scratchcpp/value.h

Lines changed: 100 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -257,24 +257,7 @@ class LIBSCRATCHCPP_EXPORT Value
257257
case Type::Integer:
258258
return std::to_string(m_intValue);
259259
case Type::Double: {
260-
std::stringstream stream;
261-
stream << m_doubleValue;
262-
std::string s = stream.str();
263-
std::size_t index;
264-
265-
for (int i = 0; i < 2; i++) {
266-
if (i == 0)
267-
index = s.find("e+");
268-
else
269-
index = s.find("e-");
270-
271-
if (index != std::string::npos) {
272-
while ((s.size() >= index + 3) && (s[index + 2] == '0'))
273-
s.erase(index + 2, 1);
274-
}
275-
}
276-
277-
return s;
260+
return doubleToString(m_doubleValue);
278261
}
279262
case Type::Bool:
280263
return m_boolValue ? "true" : "false";
@@ -471,6 +454,26 @@ class LIBSCRATCHCPP_EXPORT Value
471454
std::string m_stringValue;
472455
};
473456

457+
// -1 - error
458+
// 0 - is string
459+
// 1 - is long
460+
// 2 - is double
461+
static int checkString(const std::string &str, long *longValue, double *doubleValue)
462+
{
463+
if (!longValue || !doubleValue)
464+
return -1;
465+
466+
bool ok;
467+
468+
if ((str.find_first_of('.') == std::string::npos) && (str.find_first_of('e') == std::string::npos) && (str.find_first_of('E') == std::string::npos)) {
469+
*longValue = stringToLong(str, &ok);
470+
return ok ? 1 : 0;
471+
} else {
472+
*doubleValue = stringToDouble(str, &ok);
473+
return ok ? 2 : 0;
474+
}
475+
}
476+
474477
void initString(const std::string &str)
475478
{
476479
if (str.empty())
@@ -486,23 +489,21 @@ class LIBSCRATCHCPP_EXPORT Value
486489
return;
487490
}
488491

489-
bool ok;
490-
bool isLong = false;
491492
long l;
492493
double d;
493-
if ((str.find_first_of('.') == std::string::npos) && (str.find_first_of('e') == std::string::npos) && (str.find_first_of('E') == std::string::npos)) {
494-
l = stringToLong(str, &ok);
495-
isLong = true;
496-
} else
497-
d = stringToDouble(str, &ok);
498-
if (ok) {
499-
if (isLong) {
494+
int type = checkString(str, &l, &d);
495+
496+
switch (type) {
497+
case 1:
500498
*this = l;
501499
m_type = Type::Integer;
502-
} else {
500+
break;
501+
case 2:
503502
*this = d;
504503
m_type = Type::Double;
505-
}
504+
break;
505+
default:
506+
break;
506507
}
507508
}
508509

@@ -529,9 +530,30 @@ class LIBSCRATCHCPP_EXPORT Value
529530
}
530531
}
531532
} else {
532-
if (v1.isString() || v2.isString())
533-
return stringsEqual(v1.toUtf16(), v2.toUtf16());
534-
else if (v1.isNumber() || v2.isNumber())
533+
if (v1.isString() || v2.isString()) {
534+
if (static_cast<int>(v1.m_type) < 0 || static_cast<int>(v2.m_type) < 0)
535+
return stringsEqual(v1.toUtf16(), v2.toUtf16());
536+
537+
long l1, l2;
538+
double d1, d2;
539+
int type1 = checkString(v1.toString(), &l1, &d1);
540+
int type2 = checkString(v2.toString(), &l2, &d2);
541+
542+
if (type1 == 1)
543+
d1 = l1;
544+
545+
if (type2 == 1)
546+
d2 = l2;
547+
548+
if (type1 > 0 && type2 > 0)
549+
return d1 == d2;
550+
else if (type2 > 0)
551+
return v1.toString() == doubleToString(d2);
552+
else if (type1 > 0)
553+
return doubleToString(d1) == v2.toString();
554+
else
555+
return stringsEqual(v1.toUtf16(), v2.toUtf16());
556+
} else if (v1.isNumber() || v2.isNumber())
535557
return v1.toDouble() == v2.toDouble();
536558
else if (v1.isBool() || v2.isBool())
537559
return ((v1.m_type != Type::NaN && v2.m_type != Type::NaN) && (v1.toBool() == v2.toBool()));
@@ -708,15 +730,37 @@ class LIBSCRATCHCPP_EXPORT Value
708730
}
709731

710732
static const std::string digits = "0123456789.eE+-";
711-
for (char c : s) {
733+
const std::string *stringPtr = &s;
734+
bool customStr = false;
735+
736+
if (!s.empty() && ((s[0] == ' ') || (s.back() == ' '))) {
737+
std::string *localPtr = new std::string(s);
738+
stringPtr = localPtr;
739+
customStr = true;
740+
741+
while (!localPtr->empty() && (localPtr->at(0) == ' '))
742+
localPtr->erase(0, 1);
743+
744+
while (!localPtr->empty() && (localPtr->back() == ' '))
745+
localPtr->pop_back();
746+
}
747+
748+
for (char c : *stringPtr) {
712749
if (digits.find(c) == std::string::npos) {
713750
return 0;
714751
}
715752
}
753+
716754
try {
717755
if (ok)
718756
*ok = true;
719-
return std::stod(s);
757+
758+
double ret = std::stod(*stringPtr);
759+
760+
if (customStr)
761+
delete stringPtr;
762+
763+
return ret;
720764
} catch (...) {
721765
if (ok)
722766
*ok = false;
@@ -761,6 +805,28 @@ class LIBSCRATCHCPP_EXPORT Value
761805
return 0;
762806
}
763807
}
808+
809+
static std::string doubleToString(double v)
810+
{
811+
std::stringstream stream;
812+
stream << v;
813+
std::string s = stream.str();
814+
std::size_t index;
815+
816+
for (int i = 0; i < 2; i++) {
817+
if (i == 0)
818+
index = s.find("e+");
819+
else
820+
index = s.find("e-");
821+
822+
if (index != std::string::npos) {
823+
while ((s.size() >= index + 3) && (s[index + 2] == '0'))
824+
s.erase(index + 2, 1);
825+
}
826+
}
827+
828+
return s;
829+
}
764830
};
765831

766832
} // namespace libscratchcpp

test/scratch_classes/value_test.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,16 +1786,44 @@ TEST(ValueTest, EqualityOperators)
17861786
{
17871787
Value v1 = 5.25;
17881788
Value v2 = "5.25";
1789-
Value v3 = "5.26";
1789+
Value v3 = " 5.25";
1790+
Value v4 = "5.25 ";
1791+
Value v5 = " 5.25 ";
1792+
Value v6 = " 5.25 ";
1793+
Value v7 = "5.26";
17901794

17911795
ASSERT_TRUE(v1 == v2);
17921796
ASSERT_FALSE(v1 != v2);
17931797

1794-
ASSERT_FALSE(v1 == v3);
1795-
ASSERT_TRUE(v1 != v3);
1798+
ASSERT_TRUE(v1 == v3);
1799+
ASSERT_FALSE(v1 != v3);
17961800

1797-
ASSERT_FALSE(v2 == v3);
1798-
ASSERT_TRUE(v2 != v3);
1801+
ASSERT_TRUE(v1 == v4);
1802+
ASSERT_FALSE(v1 != v4);
1803+
1804+
ASSERT_TRUE(v1 == v5);
1805+
ASSERT_FALSE(v1 != v5);
1806+
1807+
ASSERT_TRUE(v1 == v6);
1808+
ASSERT_FALSE(v1 != v6);
1809+
1810+
ASSERT_FALSE(v1 == v7);
1811+
ASSERT_TRUE(v1 != v7);
1812+
1813+
ASSERT_FALSE(v2 == v7);
1814+
ASSERT_TRUE(v2 != v7);
1815+
1816+
ASSERT_FALSE(v3 == v7);
1817+
ASSERT_TRUE(v3 != v7);
1818+
1819+
ASSERT_FALSE(v4 == v7);
1820+
ASSERT_TRUE(v4 != v7);
1821+
1822+
ASSERT_FALSE(v5 == v7);
1823+
ASSERT_TRUE(v5 != v7);
1824+
1825+
ASSERT_FALSE(v6 == v7);
1826+
ASSERT_TRUE(v6 != v7);
17991827
}
18001828

18011829
{

0 commit comments

Comments
 (0)