From 8092e35287edd9ae9f28b9b5f688707cb68e3631 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carl=20=C3=85stholm?= <[EMAIL REDACTED]>
Date: Thu, 12 Sep 2024 01:23:56 +0200
Subject: [PATCH] stdlib: Improve SDL_strtod
- Handle leading whitespace
- Handle positive sign
- Parse integer part as unsigned long long
- Handle signed zero (this also applies to printf)
---
src/stdlib/SDL_string.c | 55 ++++++++++++++++++++++-------------------
1 file changed, 29 insertions(+), 26 deletions(-)
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 07a58609b61a0..9b086357f4998 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -540,7 +540,7 @@ static size_t SDL_ScanLongW(const wchar_t *text, int count, int radix, long *val
}
#endif
-#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOUL) || !defined(HAVE_STRTOD)
+#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOUL)
static size_t SDL_ScanUnsignedLong(const char *text, int count, int radix, unsigned long *valuep)
{
const unsigned long ulong_max = ~0UL;
@@ -608,7 +608,7 @@ static size_t SDL_ScanLongLong(const char *text, int count, int radix, long long
}
#endif
-#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL)
+#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL) || !defined(HAVE_STRTOD)
static size_t SDL_ScanUnsignedLongLong(const char *text, int count, int radix, unsigned long long *valuep)
{
const unsigned long long ullong_max = ~0ULL;
@@ -628,35 +628,40 @@ static size_t SDL_ScanUnsignedLongLong(const char *text, int count, int radix, u
#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOD)
static size_t SDL_ScanFloat(const char *text, double *valuep)
{
- const char *textstart = text;
- unsigned long lvalue = 0;
+ const char *text_start = text;
+ const char *number_start = text_start;
double value = 0.0;
bool negative = false;
- if (*text == '-') {
- negative = true;
+ while (SDL_isspace(*text)) {
++text;
}
- text += SDL_ScanUnsignedLong(text, 0, 10, &lvalue);
- value += lvalue;
- if (*text == '.') {
- int mult = 10;
+ if (*text == '-' || *text == '+') {
+ negative = *text == '-';
++text;
- while (SDL_isdigit((unsigned char)*text)) {
- lvalue = *text - '0';
- value += (double)lvalue / mult;
- mult *= 10;
+ }
+ number_start = text;
+ if (SDL_isdigit(*text)) {
+ value += SDL_strtoull(text, (char **)(&text), 10);
+ if (*text == '.') {
+ double denom = 10;
++text;
+ while (SDL_isdigit(*text)) {
+ value += (double)(*text - '0') / denom;
+ denom *= 10;
+ ++text;
+ }
}
}
- if (valuep && text > textstart) {
- if (negative && value != 0.0) {
- *valuep = -value;
- } else {
- *valuep = value;
- }
+ if (text == number_start) {
+ // no number was parsed, and thus no characters were consumed
+ text = text_start;
+ }
+ if (negative) {
+ value = -value;
}
- return text - textstart;
+ *valuep = value;
+ return text - text_start;
}
#endif
@@ -1302,10 +1307,8 @@ double SDL_strtod(const char *string, char **endp)
#ifdef HAVE_STRTOD
return strtod(string, endp);
#else
- size_t len;
- double value = 0.0;
-
- len = SDL_ScanFloat(string, &value);
+ double value;
+ size_t len = SDL_ScanFloat(string, &value);
if (endp) {
*endp = (char *)string + len;
}
@@ -1977,7 +1980,7 @@ static size_t SDL_PrintFloat(char *text, size_t maxlen, SDL_FormatInfo *info, do
// This isn't especially accurate, but hey, it's easy. :)
unsigned long long value;
- if (arg < 0) {
+ if (arg < 0.0 || (arg == 0.0 && 1.0 / arg < 0.0)) { // additional check for signed zero
num[length++] = '-';
arg = -arg;
} else if (info->force_sign) {