* lib/stdlib.in.h (strtof): New declaration.
* lib/strtod.c: Support USE_FLOAT.
* lib/strtof.c: New file.
* m4/strtof.m4: New file, based on m4/strtod.m4.
* m4/ldexpf.m4 (gl_CHECK_LDEXPF_NO_LIBM): New macro, based on
m4/ldexp.m4.
* m4/stdlib_h.m4 (gl_STDLIB_H_REQUIRE_DEFAULTS): Initialize
GNULIB_STRTOF.
* modules/stdlib (Makefile.am): Substitute GNULIB_STRTOF, HAVE_STRTOF,
REPLACE_STRTOF.
* modules/strtof: New file.
* tests/test-stdlib-c++.cc (strtof): Check signature.
* doc/posix-functions/strtof.texi: Mention the new module and the bugs
that it fixes.
(gl_STDLIB_H_DEFAULTS): Initialize HAVE_STRTOF, REPLACE_STRTOF.
+2024-02-21 Bruno Haible <bruno@clisp.org>
+
+ strtof: New module.
+ * lib/stdlib.in.h (strtof): New declaration.
+ * lib/strtod.c: Support USE_FLOAT.
+ * lib/strtof.c: New file.
+ * m4/strtof.m4: New file, based on m4/strtod.m4.
+ * m4/ldexpf.m4 (gl_CHECK_LDEXPF_NO_LIBM): New macro, based on
+ m4/ldexp.m4.
+ * m4/stdlib_h.m4 (gl_STDLIB_H_REQUIRE_DEFAULTS): Initialize
+ GNULIB_STRTOF.
+ * modules/stdlib (Makefile.am): Substitute GNULIB_STRTOF, HAVE_STRTOF,
+ REPLACE_STRTOF.
+ * modules/strtof: New file.
+ * tests/test-stdlib-c++.cc (strtof): Check signature.
+ * doc/posix-functions/strtof.texi: Mention the new module and the bugs
+ that it fixes.
+ (gl_STDLIB_H_DEFAULTS): Initialize HAVE_STRTOF, REPLACE_STRTOF.
+
2024-02-21 Bruno Haible <bruno@clisp.org>
strtod, strtold tests: Avoid a test failure on native Windows.
POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtof.html}
-Gnulib module: ---
+Gnulib module: strtof
Portability problems fixed by Gnulib:
@itemize
+@item
+This function is missing on some platforms:
+NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, HP-UX 11, IRIX 6.5, Solaris 9, MSVC 9, Android 4.4.
+
+@item
+This function returns the wrong end pointer for @samp{-0x} on some
+platforms:
+glibc 2.4, Mac OS X 10.5, FreeBSD 6.2.
+
+@item
+This function fails to parse @samp{NaN()} on some platforms:
+glibc-2.5, FreeBSD 6.2.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
@item
-This function is missing on some platforms:
-NetBSD 3.0, OpenBSD 3.8, Minix 3.1.8, HP-UX 11, IRIX 6.5, Solaris 9, MSVC 9, Android 4.4.
+This function returns +0.0 (not @minus{}0.0) for negative underflow on some
+platforms:
+glibc 2.7, mingw, MSVC 14.
+
+@item
+This function cannot distinguish between ``nan'' and ``-nan'' on some
+platforms:
+glibc 2.7, mingw, MSVC 14.
+
+@item
+This function fails to correctly parse very long strings on some
+platforms:
+Mac OS X 10.5, FreeBSD 6.2, NetBSD 5.0, Cygwin, mingw, MSVC 14.
+
+@item
+The replacement function does not always return correctly rounded results.
@end itemize
# endif
#endif
+#if @GNULIB_STRTOF@
+ /* Parse a float from STRING, updating ENDP if appropriate. */
+# if @REPLACE_STRTOF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# define strtof rpl_strtof
+# endif
+# define GNULIB_defined_strtof_function 1
+_GL_FUNCDECL_RPL (strtof, float,
+ (const char *restrict str, char **restrict endp)
+ _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (strtof, float,
+ (const char *restrict str, char **restrict endp));
+# else
+# if !@HAVE_STRTOF@
+_GL_FUNCDECL_SYS (strtof, float,
+ (const char *restrict str, char **restrict endp)
+ _GL_ARG_NONNULL ((1)));
+# endif
+_GL_CXXALIAS_SYS (strtof, float,
+ (const char *restrict str, char **restrict endp));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (strtof);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef strtof
+# if HAVE_RAW_DECL_STRTOF
+_GL_WARN_ON_USE (strtof, "strtof is unportable - "
+ "use gnulib module strtof for portability");
+# endif
+#endif
+
#if @GNULIB_STRTOLD@
/* Parse a 'long double' from STRING, updating ENDP if appropriate. */
# if @REPLACE_STRTOLD@
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
-#if ! defined USE_LONG_DOUBLE
+#if ! (defined USE_FLOAT || defined USE_LONG_DOUBLE)
# include <config.h>
#endif
#include <ctype.h> /* isspace() */
#include <errno.h>
-#include <float.h> /* {DBL,LDBL}_{MIN,MAX} */
+#include <float.h> /* {FLT,DBL,LDBL}_{MIN,MAX} */
#include <limits.h> /* LONG_{MIN,MAX} */
#include <locale.h> /* localeconv() */
#include <math.h> /* NAN */
#undef MIN
#undef MAX
-#ifdef USE_LONG_DOUBLE
+#if defined USE_FLOAT
+# define STRTOD strtof
+# define LDEXP ldexpf
+# define HAVE_UNDERLYING_STRTOD HAVE_STRTOF
+# define DOUBLE float
+# define MIN FLT_MIN
+# define MAX FLT_MAX
+# define L_(literal) literal##f
+# if HAVE_LDEXPF_IN_LIBC
+# define USE_LDEXP 1
+# else
+# define USE_LDEXP 0
+# endif
+#elif defined USE_LONG_DOUBLE
# define STRTOD strtold
# define LDEXP ldexpl
# if defined __hpux && defined __hppa
# define MIN LDBL_MIN
# define MAX LDBL_MAX
# define L_(literal) literal##L
+# if HAVE_LDEXPL_IN_LIBC
+# define USE_LDEXP 1
+# else
+# define USE_LDEXP 0
+# endif
#else
# define STRTOD strtod
# define LDEXP ldexp
# define MIN DBL_MIN
# define MAX DBL_MAX
# define L_(literal) literal
-#endif
-
-#if (defined USE_LONG_DOUBLE ? HAVE_LDEXPM_IN_LIBC : HAVE_LDEXP_IN_LIBC)
-# define USE_LDEXP 1
-#else
-# define USE_LDEXP 0
+# if HAVE_LDEXP_IN_LIBC
+# define USE_LDEXP 1
+# else
+# define USE_LDEXP 0
+# endif
#endif
/* Return true if C is a space in the current locale, avoiding
DOUBLE
STRTOD (const char *nptr, char **endptr)
#if HAVE_UNDERLYING_STRTOD
-# ifdef USE_LONG_DOUBLE
+# if defined USE_FLOAT
+# undef strtof
+# elif defined USE_LONG_DOUBLE
# undef strtold
# else
# undef strtod
--- /dev/null
+/* Convert string to 'float'.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+ This file is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This file 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2024. */
+
+#include <config.h>
+
+#define USE_FLOAT
+#include "strtod.c"
-# ldexpf.m4 serial 2
+# ldexpf.m4 serial 3
dnl Copyright (C) 2011-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
fi
AC_SUBST([LDEXPF_LIBM])
])
+
+dnl Test whether ldexpf() can be used without linking with libm.
+dnl Set gl_cv_func_ldexpf_no_libm to 'yes' or 'no' accordingly.
+AC_DEFUN([gl_CHECK_LDEXPF_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether ldexpf() can be used without linking with libm],
+ [gl_cv_func_ldexpf_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#ifndef __NO_MATH_INLINES
+ # define __NO_MATH_INLINES 1 /* for glibc */
+ #endif
+ #include <math.h>
+ float (*funcptr) (float, int) = ldexpf;
+ float x;]],
+ [[return ldexpf (x, -1) > 0;]])],
+ [gl_cv_func_ldexpf_no_libm=yes],
+ [gl_cv_func_ldexpf_no_libm=no])
+ ])
+])
-# stdlib_h.m4 serial 76
+# stdlib_h.m4 serial 77
dnl Copyright (C) 2007-2024 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SECURE_GETENV])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SETENV])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOD])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOF])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOL])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOLD])
gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_STRTOLL])
HAVE_SETSTATE=1; AC_SUBST([HAVE_SETSTATE])
HAVE_DECL_SETSTATE=1; AC_SUBST([HAVE_DECL_SETSTATE])
HAVE_STRTOD=1; AC_SUBST([HAVE_STRTOD])
+ HAVE_STRTOF=1; AC_SUBST([HAVE_STRTOF])
HAVE_STRTOL=1; AC_SUBST([HAVE_STRTOL])
HAVE_STRTOLD=1; AC_SUBST([HAVE_STRTOLD])
HAVE_STRTOLL=1; AC_SUBST([HAVE_STRTOLL])
REPLACE_SETENV=0; AC_SUBST([REPLACE_SETENV])
REPLACE_SETSTATE=0; AC_SUBST([REPLACE_SETSTATE])
REPLACE_STRTOD=0; AC_SUBST([REPLACE_STRTOD])
+ REPLACE_STRTOF=0; AC_SUBST([REPLACE_STRTOF])
REPLACE_STRTOL=0; AC_SUBST([REPLACE_STRTOL])
REPLACE_STRTOLD=0; AC_SUBST([REPLACE_STRTOLD])
REPLACE_STRTOLL=0; AC_SUBST([REPLACE_STRTOLL])
--- /dev/null
+# strtof.m4 serial 1
+dnl Copyright (C) 2002-2003, 2006-2024 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STRTOF],
+[
+ AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ dnl Test whether strtof is declared.
+ dnl Don't call AC_FUNC_STRTOF, because it does not have the right guess
+ dnl when cross-compiling.
+ dnl Don't call AC_CHECK_FUNCS([strtof]) because it would collide with the
+ dnl ac_cv_func_strtof variable set by the AC_FUNC_STRTOF macro.
+ AC_CHECK_DECLS_ONCE([strtof])
+ if test $ac_cv_have_decl_strtof != yes; then
+ HAVE_STRTOF=0
+ fi
+ if test $HAVE_STRTOF = 1; then
+ AC_CACHE_CHECK([whether strtof obeys C99], [gl_cv_func_strtof_works],
+ [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+/* Compare two numbers with ==.
+ This is a separate function because IRIX 6.5 "cc -O" miscompiles an
+ 'x == x' test. */
+static int
+numeric_equal (float x, float y)
+{
+ return x == y;
+}
+]], [[
+ int result = 0;
+ {
+ /* In some old versions of Linux (2000 or before), strtof mis-parses
+ strings with leading '+'. */
+ const char *string = " +69";
+ char *term;
+ float value = strtof (string, &term);
+ if (value != 69 || term != (string + 4))
+ result |= 1;
+ }
+ {
+ /* Under Solaris 2.4, strtof returns the wrong value for the
+ terminating character under some conditions. */
+ const char *string = "NaN";
+ char *term;
+ strtof (string, &term);
+ if (term != string && *(term - 1) == 0)
+ result |= 2;
+ }
+ {
+ /* Older glibc and Cygwin mis-parse "-0x". */
+ const char *string = "-0x";
+ char *term;
+ float value = strtof (string, &term);
+ float zero = 0.0f;
+ if (1.0f / value != -1.0f / zero || term != (string + 2))
+ result |= 4;
+ }
+ {
+ /* Many platforms do not parse hex floats. */
+ const char *string = "0XaP+1";
+ char *term;
+ float value = strtof (string, &term);
+ if (value != 20.0f || term != (string + 6))
+ result |= 8;
+ }
+ {
+ /* Many platforms do not parse infinities. HP-UX 11.31 parses inf,
+ but mistakenly sets errno. */
+ const char *string = "inf";
+ char *term;
+ float value;
+ errno = 0;
+ value = strtof (string, &term);
+ if (value != HUGE_VAL || term != (string + 3) || errno)
+ result |= 16;
+ }
+ {
+ /* glibc 2.7 and cygwin 1.5.24 misparse "nan()". */
+ const char *string = "nan()";
+ char *term;
+ float value = strtof (string, &term);
+ if (numeric_equal (value, value) || term != (string + 5))
+ result |= 32;
+ }
+ {
+ /* darwin 10.6.1 misparses "nan(". */
+ const char *string = "nan(";
+ char *term;
+ float value = strtof (string, &term);
+ if (numeric_equal (value, value) || term != (string + 3))
+ result |= 64;
+ }
+ return result;
+]])],
+ [gl_cv_func_strtof_works=yes],
+ [gl_cv_func_strtof_works=no],
+ [dnl The last known bugs in glibc strtof(), as of this writing,
+ dnl were fixed in version 2.8
+ AC_EGREP_CPP([Lucky user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8) || (__GLIBC__ > 2)) \
+ && !defined __UCLIBC__
+ Lucky user
+ #endif
+#endif
+ ],
+ [gl_cv_func_strtof_works="guessing yes"],
+ [case "$host_os" in
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_strtof_works="guessing yes" ;;
+ # Guess yes on native Windows.
+ mingw* | windows*) gl_cv_func_strtof_works="guessing yes" ;;
+ *) gl_cv_func_strtof_works="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ ])
+ case "$gl_cv_func_strtof_works" in
+ *yes) ;;
+ *)
+ REPLACE_STRTOF=1
+ ;;
+ esac
+ fi
+])
+
+# Prerequisites of lib/strtof.c.
+AC_DEFUN([gl_PREREQ_STRTOF], [
+ AC_REQUIRE([gl_CHECK_LDEXPF_NO_LIBM])
+ if test $gl_cv_func_ldexpf_no_libm = yes; then
+ AC_DEFINE([HAVE_LDEXPF_IN_LIBC], [1],
+ [Define if the ldexpf function is available in libc.])
+ fi
+ gl_CHECK_FUNCS_ANDROID([nl_langinfo], [[#include <langinfo.h>]])
+])
-e 's/@''GNULIB_SECURE_GETENV''@/$(GNULIB_SECURE_GETENV)/g' \
-e 's/@''GNULIB_SETENV''@/$(GNULIB_SETENV)/g' \
-e 's/@''GNULIB_STRTOD''@/$(GNULIB_STRTOD)/g' \
+ -e 's/@''GNULIB_STRTOF''@/$(GNULIB_STRTOF)/g' \
-e 's/@''GNULIB_STRTOL''@/$(GNULIB_STRTOL)/g' \
-e 's/@''GNULIB_STRTOLD''@/$(GNULIB_STRTOLD)/g' \
-e 's/@''GNULIB_STRTOLL''@/$(GNULIB_STRTOLL)/g' \
-e 's|@''HAVE_SETSTATE''@|$(HAVE_SETSTATE)|g' \
-e 's|@''HAVE_DECL_SETSTATE''@|$(HAVE_DECL_SETSTATE)|g' \
-e 's|@''HAVE_STRTOD''@|$(HAVE_STRTOD)|g' \
+ -e 's|@''HAVE_STRTOF''@|$(HAVE_STRTOF)|g' \
-e 's|@''HAVE_STRTOL''@|$(HAVE_STRTOL)|g' \
-e 's|@''HAVE_STRTOLD''@|$(HAVE_STRTOLD)|g' \
-e 's|@''HAVE_STRTOLL''@|$(HAVE_STRTOLL)|g' \
-e 's|@''REPLACE_SETENV''@|$(REPLACE_SETENV)|g' \
-e 's|@''REPLACE_SETSTATE''@|$(REPLACE_SETSTATE)|g' \
-e 's|@''REPLACE_STRTOD''@|$(REPLACE_STRTOD)|g' \
+ -e 's|@''REPLACE_STRTOF''@|$(REPLACE_STRTOF)|g' \
-e 's|@''REPLACE_STRTOL''@|$(REPLACE_STRTOL)|g' \
-e 's|@''REPLACE_STRTOLD''@|$(REPLACE_STRTOLD)|g' \
-e 's|@''REPLACE_STRTOLL''@|$(REPLACE_STRTOLL)|g' \
--- /dev/null
+Description:
+strtof() function: convert string to 'float'.
+
+Files:
+lib/strtof.c
+lib/strtod.c
+m4/strtof.m4
+m4/ldexpf.m4
+
+Depends-on:
+stdlib
+c-ctype [test $HAVE_STRTOF = 0 || test $REPLACE_STRTOF = 1]
+math [test $HAVE_STRTOF = 0 || test $REPLACE_STRTOF = 1]
+stdbool [test $HAVE_STRTOF = 0 || test $REPLACE_STRTOF = 1]
+
+configure.ac:
+gl_FUNC_STRTOF
+gl_CONDITIONAL([GL_COND_OBJ_STRTOF],
+ [test $HAVE_STRTOF = 0 || test $REPLACE_STRTOF = 1])
+AM_COND_IF([GL_COND_OBJ_STRTOF], [
+ gl_PREREQ_STRTOF
+])
+gl_STDLIB_MODULE_INDICATOR([strtof])
+
+Makefile.am:
+if GL_COND_OBJ_STRTOF
+lib_SOURCES += strtof.c
+endif
+
+Include:
+<stdlib.h>
+
+License:
+LGPL
+
+Maintainer:
+all
SIGNATURE_CHECK (GNULIB_NAMESPACE::strtod, double, (const char *, char **));
#endif
+#if GNULIB_TEST_STRTOF
+SIGNATURE_CHECK (GNULIB_NAMESPACE::strtof, float, (const char *, char **));
+#endif
+
#if GNULIB_TEST_STRTOLL
SIGNATURE_CHECK (GNULIB_NAMESPACE::strtoll, long long,
(const char *, char **, int));