# HG changeset patch # User Bruno Haible # Date 1548987148 -3600 # Node ID 31ab89a208b96f3d5ce0fb42062c2ded9bd5fb23 # Parent fbfa1d6417d984a26a934b2d6775a14a2e2e115a strtod, strtold: Use the locale's decimal point. * lib/strtod.c: Include , , . (decimal_point_char): New function, copied from lib/vasnprintf.c. (parse_number): Add a radixchar argument. Use it instead of '.'. (STRTOD): Invoke decimal_point_char and pass the result to parse_number. * m4/strtod.m4 (gl_PREREQ_STRTOD): Test whether nl_langinfo exists. * m4/strtold.m4 (gl_PREREQ_STRTOLD): Likewise. * tests/test-strtod1.c: New file. * tests/test-strtod1.sh: New file. * modules/strtod-tests (Files): Add test-strtod1.{sh,c}. Add locale-fr.m4 and its dependencies. (configure.ac): Invoke gt_LOCALE_FR, gt_LOCALE_FR_UTF8. (Makefile.am): Arrange to compile test-strtod1.c and run test-strtod1.sh. * tests/test-strtold1.c: New file. * tests/test-strtold1.sh: New file. * modules/strtold-tests (Files): Add test-strtold1.{sh,c}. Add locale-fr.m4 and its dependencies. (configure.ac): Invoke gt_LOCALE_FR, gt_LOCALE_FR_UTF8. (Makefile.am): Arrange to compile test-strtold1.c and run test-strtold1.sh. diff -r fbfa1d6417d9 -r 31ab89a208b9 ChangeLog --- a/ChangeLog Fri Feb 01 02:51:20 2019 +0100 +++ b/ChangeLog Fri Feb 01 03:12:28 2019 +0100 @@ -1,3 +1,27 @@ +2019-01-31 Bruno Haible + + strtod, strtold: Use the locale's decimal point. + * lib/strtod.c: Include , , . + (decimal_point_char): New function, copied from lib/vasnprintf.c. + (parse_number): Add a radixchar argument. Use it instead of '.'. + (STRTOD): Invoke decimal_point_char and pass the result to parse_number. + * m4/strtod.m4 (gl_PREREQ_STRTOD): Test whether nl_langinfo exists. + * m4/strtold.m4 (gl_PREREQ_STRTOLD): Likewise. + * tests/test-strtod1.c: New file. + * tests/test-strtod1.sh: New file. + * modules/strtod-tests (Files): Add test-strtod1.{sh,c}. Add + locale-fr.m4 and its dependencies. + (configure.ac): Invoke gt_LOCALE_FR, gt_LOCALE_FR_UTF8. + (Makefile.am): Arrange to compile test-strtod1.c and run + test-strtod1.sh. + * tests/test-strtold1.c: New file. + * tests/test-strtold1.sh: New file. + * modules/strtold-tests (Files): Add test-strtold1.{sh,c}. Add + locale-fr.m4 and its dependencies. + (configure.ac): Invoke gt_LOCALE_FR, gt_LOCALE_FR_UTF8. + (Makefile.am): Arrange to compile test-strtold1.c and run + test-strtold1.sh. + 2019-01-31 Bruno Haible strtod, strtold tests: Simplify tests. diff -r fbfa1d6417d9 -r 31ab89a208b9 lib/strtod.c --- a/lib/strtod.c Fri Feb 01 02:51:20 2019 +0100 +++ b/lib/strtod.c Fri Feb 01 03:12:28 2019 +0100 @@ -21,13 +21,18 @@ /* Specification. */ #include -#include +#include /* isspace() */ #include -#include -#include -#include +#include /* {DBL,LDBL}_{MIN,MAX} */ +#include /* LONG_{MIN,MAX} */ +#include /* localeconv() */ +#include /* NAN */ #include -#include +#include /* sprintf() */ +#include /* strdup() */ +#if HAVE_NL_LANGINFO +# include +#endif #include "c-ctype.h" @@ -72,6 +77,28 @@ return isspace (uc) != 0; } +/* Determine the decimal-point character according to the current locale. */ +static char +decimal_point_char (void) +{ + const char *point; + /* Determine it in a multithread-safe way. We know nl_langinfo is + multithread-safe on glibc systems and Mac OS X systems, but is not required + to be multithread-safe by POSIX. sprintf(), however, is multithread-safe. + localeconv() is rarely multithread-safe. */ +#if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__)) + point = nl_langinfo (RADIXCHAR); +#elif 1 + char pointbuf[5]; + sprintf (pointbuf, "%#.0f", 1.0); + point = &pointbuf[1]; +#else + point = localeconv () -> decimal_point; +#endif + /* The decimal point is always a single byte: either '.' or ','. */ + return (point[0] != '\0' ? point[0] : '.'); +} + #if !USE_LDEXP #undef LDEXP #define LDEXP dummy_ldexp @@ -146,7 +173,8 @@ EXPCHAR. BASE is RADIX**RADIX_MULTIPLIER. */ static DOUBLE parse_number (const char *nptr, - int base, int radix, int radix_multiplier, char expchar, + int base, int radix, int radix_multiplier, char radixchar, + char expchar, char **endptr) { const char *s = nptr; @@ -163,7 +191,7 @@ { if (base == 16 ? c_isxdigit (*s) : c_isdigit (*s)) ; - else if (radixchar_ptr == NULL && *s == '.') + else if (radixchar_ptr == NULL && *s == radixchar) { /* Record that we have found the decimal point. */ radixchar_ptr = s; @@ -289,11 +317,13 @@ # endif #else # undef STRTOD -# define STRTOD(NPTR,ENDPTR) parse_number (NPTR, 10, 10, 1, 'e', ENDPTR) +# define STRTOD(NPTR,ENDPTR) \ + parse_number (NPTR, 10, 10, 1, radixchar, 'e', ENDPTR) #endif /* From here on, STRTOD refers to the underlying implementation. It needs to handle only finite unsigned decimal numbers with non-null ENDPTR. */ { + char radixchar; bool negative = false; /* The number so far. */ @@ -304,6 +334,8 @@ char *endbuf; int saved_errno = errno; + radixchar = decimal_point_char (); + /* Eat whitespace. */ while (locale_isspace (*s)) ++s; @@ -316,7 +348,7 @@ num = STRTOD (s, &endbuf); end = endbuf; - if (c_isdigit (s[*s == '.'])) + if (c_isdigit (s[*s == radixchar])) { /* If a hex float was converted incorrectly, do it ourselves. If the string starts with "0x" but does not contain digits, @@ -324,7 +356,7 @@ 'p' but no exponent, then adjust the end pointer. */ if (*s == '0' && c_tolower (s[1]) == 'x') { - if (! c_isxdigit (s[2 + (s[2] == '.')])) + if (! c_isxdigit (s[2 + (s[2] == radixchar)])) { end = s + 1; @@ -333,7 +365,7 @@ } else if (end <= s + 2) { - num = parse_number (s + 2, 16, 2, 4, 'p', &endbuf); + num = parse_number (s + 2, 16, 2, 4, radixchar, 'p', &endbuf); end = endbuf; } else @@ -349,7 +381,8 @@ { /* Not really our day, is it. Rounding errors are better than outright failure. */ - num = parse_number (s + 2, 16, 2, 4, 'p', &endbuf); + num = + parse_number (s + 2, 16, 2, 4, radixchar, 'p', &endbuf); } else { @@ -379,7 +412,7 @@ { /* Not really our day, is it. Rounding errors are better than outright failure. */ - num = parse_number (s, 10, 10, 1, 'e', &endbuf); + num = parse_number (s, 10, 10, 1, radixchar, 'e', &endbuf); } else { diff -r fbfa1d6417d9 -r 31ab89a208b9 m4/strtod.m4 --- a/m4/strtod.m4 Fri Feb 01 02:51:20 2019 +0100 +++ b/m4/strtod.m4 Fri Feb 01 03:12:28 2019 +0100 @@ -1,4 +1,4 @@ -# strtod.m4 serial 23 +# strtod.m4 serial 24 dnl Copyright (C) 2002-2003, 2006-2019 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -138,4 +138,5 @@ AC_DEFINE([HAVE_LDEXP_IN_LIBC], [1], [Define if the ldexp function is available in libc.]) fi + AC_CHECK_FUNCS([nl_langinfo]) ]) diff -r fbfa1d6417d9 -r 31ab89a208b9 m4/strtold.m4 --- a/m4/strtold.m4 Fri Feb 01 02:51:20 2019 +0100 +++ b/m4/strtold.m4 Fri Feb 01 03:12:28 2019 +0100 @@ -1,4 +1,4 @@ -# strtold.m4 serial 1 +# strtold.m4 serial 2 dnl Copyright (C) 2002-2003, 2006-2019 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -117,4 +117,5 @@ AC_DEFINE([HAVE_LDEXPL_IN_LIBC], [1], [Define if the ldexpl function is available in libc.]) fi + AC_CHECK_FUNCS([nl_langinfo]) ]) diff -r fbfa1d6417d9 -r 31ab89a208b9 modules/strtod-tests --- a/modules/strtod-tests Fri Feb 01 02:51:20 2019 +0100 +++ b/modules/strtod-tests Fri Feb 01 03:12:28 2019 +0100 @@ -1,8 +1,12 @@ Files: tests/test-strtod.c +tests/test-strtod1.sh +tests/test-strtod1.c tests/signature.h tests/minus-zero.h tests/macros.h +m4/locale-fr.m4 +m4/codeset.m4 Depends-on: float @@ -10,7 +14,15 @@ signbit configure.ac: +gt_LOCALE_FR +gt_LOCALE_FR_UTF8 Makefile.am: TESTS += test-strtod check_PROGRAMS += test-strtod + +TESTS += test-strtod1.sh +TESTS_ENVIRONMENT += \ + LOCALE_FR='@LOCALE_FR@' \ + LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' +check_PROGRAMS += test-strtod1 diff -r fbfa1d6417d9 -r 31ab89a208b9 modules/strtold-tests --- a/modules/strtold-tests Fri Feb 01 02:51:20 2019 +0100 +++ b/modules/strtold-tests Fri Feb 01 03:12:28 2019 +0100 @@ -1,8 +1,12 @@ Files: tests/test-strtold.c +tests/test-strtold1.sh +tests/test-strtold1.c tests/signature.h tests/minus-zero.h tests/macros.h +m4/locale-fr.m4 +m4/codeset.m4 Depends-on: float @@ -10,7 +14,15 @@ signbit configure.ac: +gt_LOCALE_FR +gt_LOCALE_FR_UTF8 Makefile.am: TESTS += test-strtold check_PROGRAMS += test-strtold + +TESTS += test-strtold1.sh +TESTS_ENVIRONMENT += \ + LOCALE_FR='@LOCALE_FR@' \ + LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' +check_PROGRAMS += test-strtold1 diff -r fbfa1d6417d9 -r 31ab89a208b9 tests/test-strtod1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strtod1.c Fri Feb 01 03:12:28 2019 +0100 @@ -0,0 +1,97 @@ +/* Test of strtod() in a French locale. + Copyright (C) 2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include + +#include +#include + +#include "macros.h" + +int +main (int argc, char *argv[]) +{ + /* Try to set the locale by implicitly looking at the LC_ALL environment + variable. + configure should already have checked that the locale is supported. */ + if (setlocale (LC_ALL, "") == NULL) + return 1; + + { + const char input[] = "1,"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result == 1.0); + ASSERT (ptr == input + 2); + ASSERT (errno == 0); + } + { + const char input[] = ",5"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result == 0.5); + ASSERT (ptr == input + 2); + ASSERT (errno == 0); + } + { + const char input[] = "1,5"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result == 1.5); + ASSERT (ptr == input + 3); + ASSERT (errno == 0); + } + { + const char input[] = "1.5"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result == 1.0); + ASSERT (ptr == input + 1); + ASSERT (errno == 0); + } + { + const char input[] = "123.456,789"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result == 123.0); + ASSERT (ptr == input + 3); + ASSERT (errno == 0); + } + { + const char input[] = "123,456.789"; + char *ptr; + double result; + errno = 0; + result = strtod (input, &ptr); + ASSERT (result > 123.45 && result < 123.46); + ASSERT (ptr == input + 7); + ASSERT (errno == 0); + } + + return 0; +} diff -r fbfa1d6417d9 -r 31ab89a208b9 tests/test-strtod1.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strtod1.sh Fri Feb 01 03:12:28 2019 +0100 @@ -0,0 +1,23 @@ +#!/bin/sh + +: ${LOCALE_FR=fr_FR} +: ${LOCALE_FR_UTF8=fr_FR.UTF-8} + +if test $LOCALE_FR = none && test $LOCALE_FR_UTF8 = none; then + if test -f /usr/bin/localedef; then + echo "Skipping test: no locale for testing is installed" + else + echo "Skipping test: no locale for testing is supported" + fi + exit 77 +fi + +if test $LOCALE_FR != none; then + LC_ALL=$LOCALE_FR ./test-strtod1${EXEEXT} || exit 1 +fi + +if test $LOCALE_FR_UTF8 != none; then + LC_ALL=$LOCALE_FR_UTF8 ./test-strtod1${EXEEXT} || exit 1 +fi + +exit 0 diff -r fbfa1d6417d9 -r 31ab89a208b9 tests/test-strtold1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strtold1.c Fri Feb 01 03:12:28 2019 +0100 @@ -0,0 +1,97 @@ +/* Test of strtold() in a French locale. + Copyright (C) 2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include + +#include +#include + +#include "macros.h" + +int +main (int argc, char *argv[]) +{ + /* Try to set the locale by implicitly looking at the LC_ALL environment + variable. + configure should already have checked that the locale is supported. */ + if (setlocale (LC_ALL, "") == NULL) + return 1; + + { + const char input[] = "1,"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result == 1.0L); + ASSERT (ptr == input + 2); + ASSERT (errno == 0); + } + { + const char input[] = ",5"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result == 0.5L); + ASSERT (ptr == input + 2); + ASSERT (errno == 0); + } + { + const char input[] = "1,5"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result == 1.5L); + ASSERT (ptr == input + 3); + ASSERT (errno == 0); + } + { + const char input[] = "1.5"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result == 1.0L); + ASSERT (ptr == input + 1); + ASSERT (errno == 0); + } + { + const char input[] = "123.456,789"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result == 123.0L); + ASSERT (ptr == input + 3); + ASSERT (errno == 0); + } + { + const char input[] = "123,456.789"; + char *ptr; + long double result; + errno = 0; + result = strtold (input, &ptr); + ASSERT (result > 123.45L && result < 123.46L); + ASSERT (ptr == input + 7); + ASSERT (errno == 0); + } + + return 0; +} diff -r fbfa1d6417d9 -r 31ab89a208b9 tests/test-strtold1.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-strtold1.sh Fri Feb 01 03:12:28 2019 +0100 @@ -0,0 +1,23 @@ +#!/bin/sh + +: ${LOCALE_FR=fr_FR} +: ${LOCALE_FR_UTF8=fr_FR.UTF-8} + +if test $LOCALE_FR = none && test $LOCALE_FR_UTF8 = none; then + if test -f /usr/bin/localedef; then + echo "Skipping test: no locale for testing is installed" + else + echo "Skipping test: no locale for testing is supported" + fi + exit 77 +fi + +if test $LOCALE_FR != none; then + LC_ALL=$LOCALE_FR ./test-strtold1${EXEEXT} || exit 1 +fi + +if test $LOCALE_FR_UTF8 != none; then + LC_ALL=$LOCALE_FR_UTF8 ./test-strtold1${EXEEXT} || exit 1 +fi + +exit 0