subsurface/core/strtod.cpp
Berthold Stoeger b56dd13add build: remove extern "C" linkage
No more C source files, no more necessity to use C-linkage.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2024-08-13 19:28:30 +02:00

120 lines
2.2 KiB
C++

// SPDX-License-Identifier: GPL-2.0
/*
* Sane helper for 'strtod()'.
*
* Sad that we even need this, but the C library version has
* insane locale behavior, and while the Qt "toDouble()" routines
* are better in that regard, they don't have an end pointer
* (having replaced it with the completely idiotic "ok" boolean
* pointer instead).
*
* So if you want the C locale kind of parsing, use the
* ascii_strtod() function. But if you want a more relaxed
* "Hey, Europeans are people too, even if they have locales
* with commas", use general_strtod() instead.
*/
#include <ctype.h>
#include "subsurface-string.h"
static double strtod_flags(const char *str, const char **ptr, bool no_comma)
{
char c;
const char *p = str, *ep;
double val = 0.0;
double decimal = 1.0;
bool sign = false, esign = false;
bool numbers = false, dot = false;
/* skip spaces */
while (isspace(c = *p++))
/* */;
/* optional sign */
switch (c) {
case '-':
sign = true;
/* fallthrough */
case '+':
c = *p++;
}
/* Mantissa */
for (;; c = *p++) {
if (c == '.' || (c == ',' && !no_comma)) {
if (dot)
goto done;
dot = true;
continue;
}
if (c >= '0' && c <= '9') {
numbers = true;
val = (val * 10) + (c - '0');
if (dot)
decimal *= 10;
continue;
}
if (c != 'e' && c != 'E')
goto done;
break;
}
if (!numbers)
goto done;
/* Exponent */
ep = p;
c = *ep++;
switch (c) {
case '-':
esign = true;
/* fallthrough */
case '+':
c = *ep++;
}
if (c >= '0' && c <= '9') {
p = ep;
int exponent = c - '0';
for (;;) {
c = *p++;
if (c < '0' || c > '9')
break;
exponent *= 10;
exponent += c - '0';
}
/* We're not going to bother playing games */
if (exponent > 308)
exponent = 308;
while (exponent-- > 0) {
if (esign)
decimal *= 10;
else
decimal /= 10;
}
}
done:
if (!numbers)
goto no_conversion;
if (ptr)
*ptr = p - 1;
return (sign ? -val : val) / decimal;
no_conversion:
if (ptr)
*ptr = str;
return 0.0;
}
double permissive_strtod(const char *str, const char **ptr)
{
return strtod_flags(str, ptr, false);
}
double ascii_strtod(const char *str, const char **ptr)
{
return strtod_flags(str, ptr, true);
}