Skip to content

Commit

Permalink
fix #195: Fix string to double conversion (space before number)
Browse files Browse the repository at this point in the history
  • Loading branch information
adazem009 committed Sep 28, 2023
1 parent fa31f86 commit 9c2c9ff
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 39 deletions.
134 changes: 100 additions & 34 deletions include/scratchcpp/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,24 +257,7 @@ class LIBSCRATCHCPP_EXPORT Value
case Type::Integer:
return std::to_string(m_intValue);
case Type::Double: {
std::stringstream stream;
stream << m_doubleValue;
std::string s = stream.str();
std::size_t index;

for (int i = 0; i < 2; i++) {
if (i == 0)
index = s.find("e+");
else
index = s.find("e-");

if (index != std::string::npos) {
while ((s.size() >= index + 3) && (s[index + 2] == '0'))
s.erase(index + 2, 1);
}
}

return s;
return doubleToString(m_doubleValue);
}
case Type::Bool:
return m_boolValue ? "true" : "false";
Expand Down Expand Up @@ -471,6 +454,26 @@ class LIBSCRATCHCPP_EXPORT Value
std::string m_stringValue;
};

// -1 - error
// 0 - is string
// 1 - is long
// 2 - is double
static int checkString(const std::string &str, long *longValue, double *doubleValue)
{
if (!longValue || !doubleValue)
return -1;

bool ok;

if ((str.find_first_of('.') == std::string::npos) && (str.find_first_of('e') == std::string::npos) && (str.find_first_of('E') == std::string::npos)) {
*longValue = stringToLong(str, &ok);
return ok ? 1 : 0;
} else {
*doubleValue = stringToDouble(str, &ok);
return ok ? 2 : 0;
}
}

void initString(const std::string &str)
{
if (str.empty())
Expand All @@ -486,23 +489,21 @@ class LIBSCRATCHCPP_EXPORT Value
return;
}

bool ok;
bool isLong = false;
long l;
double d;
if ((str.find_first_of('.') == std::string::npos) && (str.find_first_of('e') == std::string::npos) && (str.find_first_of('E') == std::string::npos)) {
l = stringToLong(str, &ok);
isLong = true;
} else
d = stringToDouble(str, &ok);
if (ok) {
if (isLong) {
int type = checkString(str, &l, &d);

switch (type) {
case 1:
*this = l;
m_type = Type::Integer;
} else {
break;
case 2:
*this = d;
m_type = Type::Double;
}
break;
default:
break;
}
}

Expand All @@ -529,9 +530,30 @@ class LIBSCRATCHCPP_EXPORT Value
}
}
} else {
if (v1.isString() || v2.isString())
return stringsEqual(v1.toUtf16(), v2.toUtf16());
else if (v1.isNumber() || v2.isNumber())
if (v1.isString() || v2.isString()) {
if (static_cast<int>(v1.m_type) < 0 || static_cast<int>(v2.m_type) < 0)
return stringsEqual(v1.toUtf16(), v2.toUtf16());

long l1, l2;
double d1, d2;
int type1 = checkString(v1.toString(), &l1, &d1);
int type2 = checkString(v2.toString(), &l2, &d2);

if (type1 == 1)
d1 = l1;

if (type2 == 1)
d2 = l2;

if (type1 > 0 && type2 > 0)
return d1 == d2;
else if (type2 > 0)
return v1.toString() == doubleToString(d2);
else if (type1 > 0)
return doubleToString(d1) == v2.toString();
else
return stringsEqual(v1.toUtf16(), v2.toUtf16());
} else if (v1.isNumber() || v2.isNumber())
return v1.toDouble() == v2.toDouble();
else if (v1.isBool() || v2.isBool())
return ((v1.m_type != Type::NaN && v2.m_type != Type::NaN) && (v1.toBool() == v2.toBool()));
Expand Down Expand Up @@ -708,15 +730,37 @@ class LIBSCRATCHCPP_EXPORT Value
}

static const std::string digits = "0123456789.eE+-";
for (char c : s) {
const std::string *stringPtr = &s;
bool customStr = false;

if (!s.empty() && ((s[0] == ' ') || (s.back() == ' '))) {
std::string *localPtr = new std::string(s);
stringPtr = localPtr;
customStr = true;

while (!localPtr->empty() && (localPtr->at(0) == ' '))
localPtr->erase(0, 1);

while (!localPtr->empty() && (localPtr->back() == ' '))
localPtr->pop_back();
}

for (char c : *stringPtr) {
if (digits.find(c) == std::string::npos) {
return 0;
}
}

try {
if (ok)
*ok = true;
return std::stod(s);

double ret = std::stod(*stringPtr);

if (customStr)
delete stringPtr;

return ret;
} catch (...) {
if (ok)
*ok = false;
Expand Down Expand Up @@ -761,6 +805,28 @@ class LIBSCRATCHCPP_EXPORT Value
return 0;
}
}

static std::string doubleToString(double v)
{
std::stringstream stream;
stream << v;
std::string s = stream.str();
std::size_t index;

for (int i = 0; i < 2; i++) {
if (i == 0)
index = s.find("e+");
else
index = s.find("e-");

if (index != std::string::npos) {
while ((s.size() >= index + 3) && (s[index + 2] == '0'))
s.erase(index + 2, 1);
}
}

return s;
}
};

} // namespace libscratchcpp
38 changes: 33 additions & 5 deletions test/scratch_classes/value_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1786,16 +1786,44 @@ TEST(ValueTest, EqualityOperators)
{
Value v1 = 5.25;
Value v2 = "5.25";
Value v3 = "5.26";
Value v3 = " 5.25";
Value v4 = "5.25 ";
Value v5 = " 5.25 ";
Value v6 = " 5.25 ";
Value v7 = "5.26";

ASSERT_TRUE(v1 == v2);
ASSERT_FALSE(v1 != v2);

ASSERT_FALSE(v1 == v3);
ASSERT_TRUE(v1 != v3);
ASSERT_TRUE(v1 == v3);
ASSERT_FALSE(v1 != v3);

ASSERT_FALSE(v2 == v3);
ASSERT_TRUE(v2 != v3);
ASSERT_TRUE(v1 == v4);
ASSERT_FALSE(v1 != v4);

ASSERT_TRUE(v1 == v5);
ASSERT_FALSE(v1 != v5);

ASSERT_TRUE(v1 == v6);
ASSERT_FALSE(v1 != v6);

ASSERT_FALSE(v1 == v7);
ASSERT_TRUE(v1 != v7);

ASSERT_FALSE(v2 == v7);
ASSERT_TRUE(v2 != v7);

ASSERT_FALSE(v3 == v7);
ASSERT_TRUE(v3 != v7);

ASSERT_FALSE(v4 == v7);
ASSERT_TRUE(v4 != v7);

ASSERT_FALSE(v5 == v7);
ASSERT_TRUE(v5 != v7);

ASSERT_FALSE(v6 == v7);
ASSERT_TRUE(v6 != v7);
}

{
Expand Down

0 comments on commit 9c2c9ff

Please sign in to comment.