fix locale-dependent decimal separators (issue #829)

This commit is contained in:
Alex Shinn 2022-05-12 18:10:38 +09:00
parent b0735b3ca7
commit 09200ae13c
2 changed files with 34 additions and 2 deletions

View file

@ -177,6 +177,10 @@
/* uncomment this if you don't want 1## style approximate digits */
/* #define SEXP_USE_PLACEHOLDER_DIGITS 0 */
/* uncomment this to disable a workaround for numeric formatting, */
/* to fix numbers in locales which don't use the '.' decimal sep */
/* #define SEXP_USE_PATCH_NON_DECIMAL_NUMERIC_FORMATS 0 */
/* uncomment this if you don't need extended math operations */
/* This includes the trigonometric and expt functions. */
/* Automatically disabled if you've disabled flonums. */
@ -665,6 +669,10 @@
#define SEXP_PLACEHOLDER_DIGIT '#'
#endif
#ifndef SEXP_USE_PATCH_NON_DECIMAL_NUMERIC_FORMATS
#define SEXP_USE_PATCH_NON_DECIMAL_NUMERIC_FORMATS 1
#endif
#ifndef SEXP_USE_MATH
#define SEXP_USE_MATH SEXP_USE_FLONUMS && ! SEXP_USE_NO_FEATURES
#endif

28
sexp.c
View file

@ -2111,7 +2111,7 @@ sexp sexp_write_one (sexp ctx, sexp obj, sexp out, sexp_sint_t bound) {
sexp_uint_t res;
#endif
sexp_uint_t len, c;
sexp_sint_t i=0;
sexp_sint_t i=0, j, k;
#if SEXP_USE_FLONUMS
double f, ftmp;
#endif
@ -2173,6 +2173,9 @@ sexp sexp_write_one (sexp ctx, sexp obj, sexp out, sexp_sint_t bound) {
} else
#endif
{
/* snprintf doesn't guarantee the shortest accurate */
/* representation, so we try successively longer formats until */
/* we find the one that scans back as the original number */
i = snprintf(numbuf, sizeof(numbuf), "%.15lg", f);
if (sscanf(numbuf, "%lg", &ftmp) == 1 && ftmp != f) {
i = snprintf(numbuf, sizeof(numbuf), "%.16lg", f);
@ -2180,7 +2183,28 @@ sexp sexp_write_one (sexp ctx, sexp obj, sexp out, sexp_sint_t bound) {
i = snprintf(numbuf, sizeof(numbuf), "%.17lg", f);
}
}
if (!strchr(numbuf, '.') && !strchr(numbuf, 'e')) {
for (j = 0; j < i; ++j) {
if (numbuf[j] == '.' || numbuf[j] == 'e') {
break;
#if SEXP_USE_PATCH_NON_DECIMAL_NUMERIC_FORMATS
} else if (!sexp_isdigit(numbuf[j]) && numbuf[j] != '-') {
/* handle the case where we're embedded in an app which has */
/* called setlocale to something which doesn't use a decimal */
/* separator (e.g. a comma), by replacing any */
/* non-digit/decimal char with a decimal */
for (k = j+1; k < i && !sexp_isdigit(numbuf[k]); ++k)
;
numbuf[j++] = '.';
while (k < i)
numbuf[j++] = numbuf[k++];
numbuf[j++] = '\0';
j = 0;
break;
}
#endif
}
/* regardless, append a decimal if there wasn't any */
if (j >= i) {
numbuf[i++] = '.'; numbuf[i++] = '0'; numbuf[i++] = '\0';
}
}