Skip to content

Commit a5f91f6

Browse files
committed
Refactor double to string conversion
1 parent 62cc8ac commit a5f91f6

File tree

3 files changed

+186
-64
lines changed

3 files changed

+186
-64
lines changed

src/scratch/value_functions.cpp

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,11 @@ extern "C"
242242
}
243243

244244
/*! Writes the string representation of the given value to dst. */
245-
void value_toString(const libscratchcpp::ValueData *v, std::string *dst)
245+
void value_toString(const ValueData *v, std::string *dst)
246246
{
247-
if (v->type == ValueType::String)
248-
dst->assign(v->stringValue);
249-
else if (v->type == ValueType::Number)
250-
value_doubleToString(v->numberValue, dst);
251-
else if (v->type == ValueType::Bool)
252-
dst->assign(v->boolValue ? "true" : "false");
253-
else
254-
dst->clear();
247+
char *str = value_toCString(v);
248+
dst->assign(str);
249+
free(str);
255250
}
256251

257252
/*!
@@ -260,11 +255,29 @@ extern "C"
260255
*/
261256
char *value_toCString(const ValueData *v)
262257
{
263-
std::string out;
264-
value_toString(v, &out);
265-
char *ret = (char *)malloc((out.size() + 1) * sizeof(char));
266-
strncpy(ret, out.c_str(), out.size() + 1);
267-
return ret;
258+
if (v->type == ValueType::String) {
259+
char *ret = (char *)malloc((strlen(v->stringValue) + 1) * sizeof(char));
260+
strcpy(ret, v->stringValue);
261+
return ret;
262+
} else if (v->type == ValueType::Number)
263+
return value_doubleToCString(v->numberValue);
264+
else if (v->type == ValueType::Bool) {
265+
char *ret;
266+
267+
if (v->boolValue) {
268+
ret = (char *)malloc((4 + 1) * sizeof(char));
269+
strcpy(ret, "true");
270+
} else {
271+
ret = (char *)malloc((5 + 1) * sizeof(char));
272+
strcpy(ret, "false");
273+
}
274+
275+
return ret;
276+
} else {
277+
char *ret = (char *)malloc(sizeof(char));
278+
ret[0] = '\0';
279+
return ret;
280+
}
268281
}
269282

270283
/*! Writes the UTF-16 representation of the given value to dst. */
@@ -292,11 +305,74 @@ extern "C"
292305
*/
293306
char *value_doubleToCString(double v)
294307
{
295-
std::string out;
296-
value_doubleToString(v, &out);
297-
char *ret = (char *)malloc((out.size() + 1) * sizeof(char));
298-
strncpy(ret, out.c_str(), out.size() + 1);
299-
return ret;
308+
if (v == 0) {
309+
char *ret = (char *)malloc((1 + 1) * sizeof(char));
310+
strcpy(ret, "0");
311+
return ret;
312+
} else if (std::isinf(v)) {
313+
if (v > 0) {
314+
char *ret = (char *)malloc((8 + 1) * sizeof(char));
315+
strcpy(ret, "Infinity");
316+
return ret;
317+
} else {
318+
char *ret = (char *)malloc((9 + 1) * sizeof(char));
319+
strcpy(ret, "-Infinity");
320+
return ret;
321+
}
322+
} else if (std::isnan(v)) {
323+
char *ret = (char *)malloc((3 + 1) * sizeof(char));
324+
strcpy(ret, "NaN");
325+
return ret;
326+
}
327+
328+
const int maxlen = 26; // should be enough for any number
329+
char *buffer = (char *)malloc((maxlen + 1) * sizeof(char));
330+
331+
// Constants for significant digits and thresholds
332+
const int significantDigits = 17 - value_intDigitCount(std::floor(std::fabs(v)));
333+
constexpr double scientificThreshold = 1e21;
334+
constexpr double minScientificThreshold = 1e-6;
335+
336+
// Use scientific notation if the number is very large or very small
337+
if (std::fabs(v) >= scientificThreshold || std::fabs(v) < minScientificThreshold) {
338+
int ret = snprintf(buffer, maxlen, "%.*g", significantDigits - 1, v);
339+
assert(ret >= 0);
340+
} else {
341+
snprintf(buffer, maxlen, "%.*f", significantDigits - 1, v);
342+
343+
// Remove trailing zeros from the fractional part
344+
char *dot = std::strchr(buffer, '.');
345+
346+
if (dot) {
347+
char *end = buffer + std::strlen(buffer) - 1;
348+
while (end > dot && *end == '0') {
349+
*end-- = '\0';
350+
}
351+
if (*end == '.') {
352+
*end = '\0'; // Remove trailing dot
353+
}
354+
}
355+
}
356+
357+
// Remove leading zeros after e+/e-
358+
for (int i = 0; i < 2; i++) {
359+
const char *target = (i == 0) ? "e+" : "e-";
360+
char *index = strstr(buffer, target);
361+
362+
if (index != nullptr) {
363+
char *ptr = index + 2;
364+
while (*(ptr + 1) != '\0' && *ptr == '0') {
365+
// Shift the characters left to erase '0'
366+
char *shiftPtr = ptr;
367+
do {
368+
*shiftPtr = *(shiftPtr + 1);
369+
shiftPtr++;
370+
} while (*shiftPtr != '\0');
371+
}
372+
}
373+
}
374+
375+
return buffer;
300376
}
301377

302378
/*!

src/scratch/value_functions_p.h

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
namespace libscratchcpp
1919
{
2020

21+
template<typename T>
22+
inline unsigned int value_intDigitCount(T v)
23+
{
24+
unsigned int i = 0;
25+
26+
while (v >= 1) {
27+
v /= 10;
28+
i++;
29+
}
30+
31+
return i;
32+
}
33+
2134
template<typename T>
2235
inline unsigned int value_digitCount(T v)
2336
{
@@ -407,51 +420,6 @@ extern "C"
407420
}
408421
}
409422

410-
inline void value_doubleToString(double v, std::string *dst)
411-
{
412-
if (v == 0) {
413-
dst->assign("0");
414-
return;
415-
} else if (std::isinf(v)) {
416-
if (v > 0)
417-
dst->assign("Infinity");
418-
else
419-
dst->assign("-Infinity");
420-
421-
return;
422-
} else if (std::isnan(v)) {
423-
dst->assign("NaN");
424-
return;
425-
}
426-
427-
std::stringstream stream;
428-
429-
if (v != 0) {
430-
const int exponent = std::log10(std::abs(v));
431-
432-
if (exponent > 20)
433-
stream << std::scientific << std::setprecision(value_digitCount(v / std::pow(10, exponent + 1)) - 1) << v;
434-
else
435-
stream << std::setprecision(std::max(16u, value_digitCount(v))) << v;
436-
} else
437-
stream << std::setprecision(std::max(16u, value_digitCount(v))) << v;
438-
439-
dst->assign(stream.str());
440-
std::size_t index;
441-
442-
for (int i = 0; i < 2; i++) {
443-
if (i == 0)
444-
index = dst->find("e+");
445-
else
446-
index = dst->find("e-");
447-
448-
if (index != std::string::npos) {
449-
while ((dst->size() >= index + 3) && ((*dst)[index + 2] == '0'))
450-
dst->erase(index + 2, 1);
451-
}
452-
}
453-
}
454-
455423
extern "C"
456424
{
457425
inline double value_floatToDouble(float v)

test/scratch_classes/value_test.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,30 @@ TEST(ValueTest, ToString)
14011401
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
14021402
ASSERT_EQ(std::string(cStrings.back()), v.toString());
14031403

1404+
v = 59.8;
1405+
cStrings.push_back(value_toCString(&v.data()));
1406+
ASSERT_EQ(v.toString(), "59.8");
1407+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1408+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1409+
1410+
v = -59.8;
1411+
cStrings.push_back(value_toCString(&v.data()));
1412+
ASSERT_EQ(v.toString(), "-59.8");
1413+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1414+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1415+
1416+
v = 5.3;
1417+
cStrings.push_back(value_toCString(&v.data()));
1418+
ASSERT_EQ(v.toString(), "5.3");
1419+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1420+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1421+
1422+
v = -5.3;
1423+
cStrings.push_back(value_toCString(&v.data()));
1424+
ASSERT_EQ(v.toString(), "-5.3");
1425+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1426+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1427+
14041428
v = 2550.625021000115;
14051429
cStrings.push_back(value_toCString(&v.data()));
14061430
ASSERT_EQ(v.toString(), "2550.625021000115");
@@ -1448,6 +1472,28 @@ TEST(ValueTest, ToString)
14481472
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
14491473
ASSERT_EQ(std::string(cStrings.back()), v.toString());
14501474

1475+
v = 0.000001;
1476+
cStrings.push_back(value_toCString(&v.data()));
1477+
ASSERT_EQ(v.toString(), "0.000001");
1478+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1479+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1480+
v = -0.000001;
1481+
cStrings.push_back(value_toCString(&v.data()));
1482+
ASSERT_EQ(v.toString(), "-0.000001");
1483+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1484+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1485+
1486+
v = 0.0000001;
1487+
cStrings.push_back(value_toCString(&v.data()));
1488+
ASSERT_EQ(v.toString(), "1e-7");
1489+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1490+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1491+
v = -0.0000001;
1492+
cStrings.push_back(value_toCString(&v.data()));
1493+
ASSERT_EQ(v.toString(), "-1e-7");
1494+
ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString());
1495+
ASSERT_EQ(std::string(cStrings.back()), v.toString());
1496+
14511497
v = false;
14521498
cStrings.push_back(value_toCString(&v.data()));
14531499
ASSERT_EQ(v.toString(), "false");
@@ -2846,6 +2892,22 @@ TEST(ValueTest, DoubleToCString)
28462892
ASSERT_EQ(strcmp(ret, "-2.54"), 0);
28472893
free(ret);
28482894

2895+
ret = value_doubleToCString(59.8);
2896+
ASSERT_EQ(strcmp(ret, "59.8"), 0);
2897+
free(ret);
2898+
2899+
ret = value_doubleToCString(-59.8);
2900+
ASSERT_EQ(strcmp(ret, "-59.8"), 0);
2901+
free(ret);
2902+
2903+
ret = value_doubleToCString(5.3);
2904+
ASSERT_EQ(strcmp(ret, "5.3"), 0);
2905+
free(ret);
2906+
2907+
ret = value_doubleToCString(-5.3);
2908+
ASSERT_EQ(strcmp(ret, "-5.3"), 0);
2909+
free(ret);
2910+
28492911
ret = value_doubleToCString(2550.625021000115);
28502912
ASSERT_EQ(strcmp(ret, "2550.625021000115"), 0);
28512913
free(ret);
@@ -2878,6 +2940,22 @@ TEST(ValueTest, DoubleToCString)
28782940
ASSERT_EQ(strcmp(ret, "-0.001"), 0);
28792941
free(ret);
28802942

2943+
ret = value_doubleToCString(0.000001);
2944+
ASSERT_EQ(strcmp(ret, "0.000001"), 0);
2945+
free(ret);
2946+
2947+
ret = value_doubleToCString(-0.000001);
2948+
ASSERT_EQ(strcmp(ret, "-0.000001"), 0);
2949+
free(ret);
2950+
2951+
ret = value_doubleToCString(0.0000001);
2952+
ASSERT_EQ(strcmp(ret, "1e-7"), 0);
2953+
free(ret);
2954+
2955+
ret = value_doubleToCString(-0.0000001);
2956+
ASSERT_EQ(strcmp(ret, "-1e-7"), 0);
2957+
free(ret);
2958+
28812959
ret = value_doubleToCString(std::numeric_limits<double>::infinity());
28822960
ASSERT_EQ(strcmp(ret, "Infinity"), 0);
28832961
free(ret);

0 commit comments

Comments
 (0)