Parse number locale-independently

Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.

We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).

So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui
2019-02-02 01:22:41 +00:00
parent 3f5a4d570c
commit 465a968ddd
4 changed files with 41 additions and 3 deletions

View File

@ -47,8 +47,8 @@ parse_long(const char *s, long *dest) {
*/
const char *
parse_matrix_readnum(const char *src, double *dest) {
char *pc = NULL;
double val = strtod(src, &pc);
const char *pc = NULL;
double val = strtod_simple(src, &pc);
if (!pc || pc == src) {
log_error("No number found: %s", src);
return src;

View File

@ -40,3 +40,34 @@ void mstrextend(char **psrc1, const char *src2) {
strncpy(*psrc1 + len1, src2, len2);
(*psrc1)[len - 1] = '\0';
}
/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
double strtod_simple(const char *src, const char **end) {
double neg = 1;
if (*src == '-') {
neg = -1;
src++;
} else if (*src == '+') {
src++;
}
double ret = 0;
while (*src >= '0' && *src <= '9') {
ret = ret * 10 + (*src - '0');
src++;
}
if (*src == '.') {
double frac = 0, mult = 0.1;
src++;
while (*src >= '0' && *src <= '9') {
frac += mult * (*src - '0');
mult *= 0.1;
src++;
}
ret += frac;
}
*end = src;
return ret * neg;
}

View File

@ -11,6 +11,9 @@ char *
mstrjoin3(const char *src1, const char *src2, const char *src3);
void mstrextend(char **psrc1, const char *src2);
/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
double strtod_simple(const char *, const char **);
static inline int uitostr(unsigned int n, char *buf) {
int ret = 0;
unsigned int tmp = n;