* new module 'strtof'
@ 2024-02-22 0:58 Bruno Haible
0 siblings, 0 replies; only message in thread
From: Bruno Haible @ 2024-02-22 0:58 UTC (permalink / raw)
To: bug-gnulib
[-- Attachment #1: Type: text/plain, Size: 1069 bytes --]
These two patches add a module 'strtof', similar to 'strtod' and 'strtold'.
2024-02-21 Bruno Haible <bruno@clisp.org>
strtof: Add tests.
* tests/test-strtof.c: New file, based on tests/test-strtod.c.
* tests/test-strtof1.sh: New file, based on tests/test-strtod1.sh.
* tests/test-strtof1.c: New file, based on tests/test-strtod1.c.
* modules/strtof-tests: New file, based on modules/strtod-tests.
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.
[-- Attachment #2: 0001-strtof-New-module.patch --]
[-- Type: text/x-patch, Size: 19138 bytes --]
From 01a485ed9b3b2ce3d533581fbca94970bdef9998 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Wed, 21 Feb 2024 22:45:47 +0100
Subject: [PATCH 1/4] 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.
---
ChangeLog | 19 +++++
doc/posix-functions/strtof.texi | 32 ++++++-
lib/stdlib.in.h | 32 +++++++
lib/strtod.c | 39 ++++++---
lib/strtof.c | 22 +++++
m4/ldexpf.m4 | 22 ++++-
m4/stdlib_h.m4 | 5 +-
m4/strtof.m4 | 142 ++++++++++++++++++++++++++++++++
modules/stdlib | 3 +
modules/strtof | 37 +++++++++
tests/test-stdlib-c++.cc | 4 +
11 files changed, 342 insertions(+), 15 deletions(-)
create mode 100644 lib/strtof.c
create mode 100644 m4/strtof.m4
create mode 100644 modules/strtof
diff --git a/ChangeLog b/ChangeLog
index 767644ef1b..6df8be5ada 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+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.
diff --git a/doc/posix-functions/strtof.texi b/doc/posix-functions/strtof.texi
index bf19d2ad3b..08d69ec5c6 100644
--- a/doc/posix-functions/strtof.texi
+++ b/doc/posix-functions/strtof.texi
@@ -4,15 +4,41 @@
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
diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h
index b901d175ae..e74e7c18d1 100644
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
@@ -1591,6 +1591,38 @@ _GL_WARN_ON_USE (strtod, "strtod is unportable - "
# 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@
diff --git a/lib/strtod.c b/lib/strtod.c
index c744d2f43b..a545be09a4 100644
--- a/lib/strtod.c
+++ b/lib/strtod.c
@@ -14,7 +14,7 @@
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
@@ -23,7 +23,7 @@
#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 */
@@ -37,7 +37,20 @@
#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
@@ -54,6 +67,11 @@
# 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
@@ -62,12 +80,11 @@
# 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
@@ -311,7 +328,9 @@ minus_zero (void)
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
diff --git a/lib/strtof.c b/lib/strtof.c
new file mode 100644
index 0000000000..194962ad32
--- /dev/null
+++ b/lib/strtof.c
@@ -0,0 +1,22 @@
+/* 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"
diff --git a/m4/ldexpf.m4 b/m4/ldexpf.m4
index 9297ae3fc9..b6cdcb4a59 100644
--- a/m4/ldexpf.m4
+++ b/m4/ldexpf.m4
@@ -1,4 +1,4 @@
-# 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,
@@ -23,3 +23,23 @@ AC_DEFUN([gl_FUNC_LDEXPF]
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])
+ ])
+])
diff --git a/m4/stdlib_h.m4 b/m4/stdlib_h.m4
index 92e67a74bb..88ccd14137 100644
--- a/m4/stdlib_h.m4
+++ b/m4/stdlib_h.m4
@@ -1,4 +1,4 @@
-# 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,
@@ -145,6 +145,7 @@ AC_DEFUN([gl_STDLIB_H_REQUIRE_DEFAULTS]
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])
@@ -205,6 +206,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS]
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])
@@ -248,6 +250,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS]
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])
diff --git a/m4/strtof.m4 b/m4/strtof.m4
new file mode 100644
index 0000000000..483fa107a3
--- /dev/null
+++ b/m4/strtof.m4
@@ -0,0 +1,142 @@
+# 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>]])
+])
diff --git a/modules/stdlib b/modules/stdlib
index baf4b29a8c..7b0194db41 100644
--- a/modules/stdlib
+++ b/modules/stdlib
@@ -73,6 +73,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_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' \
@@ -125,6 +126,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-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' \
@@ -170,6 +172,7 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \
-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' \
diff --git a/modules/strtof b/modules/strtof
new file mode 100644
index 0000000000..12f5258366
--- /dev/null
+++ b/modules/strtof
@@ -0,0 +1,37 @@
+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
diff --git a/tests/test-stdlib-c++.cc b/tests/test-stdlib-c++.cc
index 55a71a6cc6..0b02ca6e75 100644
--- a/tests/test-stdlib-c++.cc
+++ b/tests/test-stdlib-c++.cc
@@ -187,6 +187,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::setenv, int,
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));
--
2.34.1
[-- Attachment #3: 0002-strtof-Add-tests.patch --]
[-- Type: text/x-patch, Size: 35822 bytes --]
From 471a56952835d2d586c729434262d6cd40eb8e09 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Thu, 22 Feb 2024 01:27:30 +0100
Subject: [PATCH 2/4] strtof: Add tests.
* tests/test-strtof.c: New file, based on tests/test-strtod.c.
* tests/test-strtof1.sh: New file, based on tests/test-strtod1.sh.
* tests/test-strtof1.c: New file, based on tests/test-strtod1.c.
* modules/strtof-tests: New file, based on modules/strtod-tests.
---
ChangeLog | 6 +
modules/strtof-tests | 31 ++
tests/test-strtof.c | 980 ++++++++++++++++++++++++++++++++++++++++++
tests/test-strtof1.c | 101 +++++
tests/test-strtof1.sh | 30 ++
5 files changed, 1148 insertions(+)
create mode 100644 modules/strtof-tests
create mode 100644 tests/test-strtof.c
create mode 100644 tests/test-strtof1.c
create mode 100755 tests/test-strtof1.sh
diff --git a/ChangeLog b/ChangeLog
index 6df8be5ada..475c3f16f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2024-02-21 Bruno Haible <bruno@clisp.org>
+ strtof: Add tests.
+ * tests/test-strtof.c: New file, based on tests/test-strtod.c.
+ * tests/test-strtof1.sh: New file, based on tests/test-strtod1.sh.
+ * tests/test-strtof1.c: New file, based on tests/test-strtod1.c.
+ * modules/strtof-tests: New file, based on modules/strtod-tests.
+
strtof: New module.
* lib/stdlib.in.h (strtof): New declaration.
* lib/strtod.c: Support USE_FLOAT.
diff --git a/modules/strtof-tests b/modules/strtof-tests
new file mode 100644
index 0000000000..00e5df709b
--- /dev/null
+++ b/modules/strtof-tests
@@ -0,0 +1,31 @@
+Files:
+tests/test-strtof.c
+tests/test-strtof1.sh
+tests/test-strtof1.c
+tests/signature.h
+tests/minus-zero.h
+tests/macros.h
+m4/locale-fr.m4
+m4/codeset.m4
+
+Depends-on:
+float
+isnanf-nolibm
+signbit
+setlocale
+
+configure.ac:
+gt_LOCALE_FR
+gt_LOCALE_FR_UTF8
+
+Makefile.am:
+TESTS += test-strtof
+check_PROGRAMS += test-strtof
+
+TESTS += test-strtof1.sh
+TESTS_ENVIRONMENT += \
+ LOCALE_FR='@LOCALE_FR@' \
+ LOCALE_FR_UTF8='@LOCALE_FR_UTF8@' \
+ LC_NUMERIC_IMPLEMENTED='@LC_NUMERIC_IMPLEMENTED@'
+check_PROGRAMS += test-strtof1
+test_strtof1_LDADD = $(LDADD) $(SETLOCALE_LIB)
diff --git a/tests/test-strtof.c b/tests/test-strtof.c
new file mode 100644
index 0000000000..84f60dbcd7
--- /dev/null
+++ b/tests/test-strtof.c
@@ -0,0 +1,980 @@
+/*
+ * Copyright (C) 2008-2024 Free Software Foundation, Inc.
+ * Written by Eric Blake
+ *
+ * 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 <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (strtof, float, (char const *, char **));
+
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#include "isnanf-nolibm.h"
+#include "minus-zero.h"
+#include "macros.h"
+
+/* Avoid requiring -lm just for fabsf. */
+#define FABS(f) ((f) < 0.0f ? -(f) : (f))
+
+int
+main (void)
+{
+ int status = 0;
+ /* Subject sequence empty or invalid. */
+ {
+ const char input[] = "";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " ";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " +";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " .";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " .e0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input); /* IRIX 6.5, OSF/1 5.1 */
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " +.e-0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input); /* IRIX 6.5, OSF/1 5.1 */
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " in";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+ {
+ const char input[] = " na";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+ }
+
+ /* Simple floating point values. */
+ {
+ const char input[] = "1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1.";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = ".5";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.5f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = " 1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "-1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == -1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e+0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e-0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 10.0f);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "5e-1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.5f);
+ ASSERT (ptr == input + 4);
+ ASSERT (errno == 0);
+ }
+
+ /* Zero. */
+ {
+ const char input[] = "0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = ".0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0e0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0e+9999999";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0e-9999999";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "-0";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!!signbit (result) == !!signbit (minus_zerof)); /* IRIX 6.5, OSF/1 4.0 */
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+
+ /* Suffixes. */
+ {
+ const char input[] = "1f";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1.f";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e+";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1e-";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1E 2";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* HP-UX 11.11, IRIX 6.5, OSF/1 4.0 */
+ ASSERT (ptr == input + 1); /* HP-UX 11.11, IRIX 6.5 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "00x1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "-0x";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!!signbit (result) == !!signbit (minus_zerof)); /* Mac OS X 10.3, FreeBSD 6.2, IRIX 6.5, OSF/1 4.0 */
+ ASSERT (ptr == input + 2); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0xg";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0xp";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0XP";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x.";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0xp+";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0xp+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x.p+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 1); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, AIX 7.1 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1p+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1P+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 1);
+ ASSERT (errno == 0);
+ }
+
+ /* Overflow/underflow. */
+ {
+ const char input[] = "1E1000000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == HUGE_VAL);
+ ASSERT (ptr == input + 9); /* OSF/1 5.1 */
+ ASSERT (errno == ERANGE);
+ }
+ {
+ const char input[] = "-1E1000000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == -HUGE_VAL);
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ const char input[] = "1E-100000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (0.0f <= result && result <= FLT_MIN);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input + 9);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ const char input[] = "-1E-100000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (-FLT_MIN <= result && result <= 0.0f);
+#if 0
+ /* FIXME - this is glibc bug 5995; POSIX allows returning positive
+ 0 on negative underflow, even though quality of implementation
+ demands preserving the sign. Disable this test until fixed
+ glibc is more prevalent. */
+ ASSERT (!!signbit (result) == !!signbit (minus_zerof)); /* glibc-2.3.6, mingw */
+#endif
+ ASSERT (ptr == input + 10);
+ ASSERT (errno == ERANGE);
+ }
+ {
+ const char input[] = "1E 1000000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* HP-UX 11.11, IRIX 6.5, OSF/1 4.0 */
+ ASSERT (ptr == input + 1); /* HP-UX 11.11, IRIX 6.5 */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1P 1000000";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+
+ /* Infinity. */
+ {
+ const char input[] = "iNf";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == HUGE_VAL); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 3); /* OpenBSD 4.0, HP-UX 11.00, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw */
+ ASSERT (errno == 0); /* HP-UX 11.11, OSF/1 4.0 */
+ }
+ {
+ const char input[] = "-InF";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == -HUGE_VAL); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 4); /* OpenBSD 4.0, HP-UX 11.00, IRIX 6.5, OSF/1 4.0, Solaris 9, mingw */
+ ASSERT (errno == 0); /* HP-UX 11.11, OSF/1 4.0 */
+ }
+ {
+ const char input[] = "infinite";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == HUGE_VAL); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 3); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (errno == 0); /* OSF/1 4.0 */
+ }
+ {
+ const char input[] = "infinitY";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == HUGE_VAL); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 8); /* OpenBSD 4.0, HP-UX 11.00, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw */
+ ASSERT (errno == 0); /* HP-UX 11.11, OSF/1 4.0 */
+ }
+ {
+ const char input[] = "infinitY.";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == HUGE_VAL); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 8); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (errno == 0); /* OSF/1 4.0 */
+ }
+
+ /* NaN. Some processors set the sign bit of the default NaN, so all
+ we check is that using a sign changes the result. */
+ {
+ const char input[] = "-nan";
+ char *ptr1;
+ char *ptr2;
+ float result1;
+ float result2;
+ errno = 0;
+ result1 = strtof (input, &ptr1);
+ result2 = strtof (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs. */
+ ASSERT (isnanf (result1)); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (isnanf (result2)); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+# if 0
+ /* Sign bits of NaN is a portability sticking point, not worth
+ worrying about. */
+ ASSERT (!!signbit (result1) != !!signbit (result2)); /* glibc-2.3.6, IRIX 6.5, OSF/1 5.1, mingw */
+# endif
+ ASSERT (ptr1 == input + 4); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, Solaris 2.5.1, mingw */
+ ASSERT (ptr2 == input + 4); /* OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, Solaris 2.5.1, mingw */
+ ASSERT (errno == 0); /* HP-UX 11.11 */
+#else
+ ASSERT (result1 == 0.0f);
+ ASSERT (result2 == 0.0f);
+ ASSERT (!signbit (result1));
+ ASSERT (!signbit (result2));
+ ASSERT (ptr1 == input);
+ ASSERT (ptr2 == input + 1);
+ ASSERT (errno == 0 || errno == EINVAL);
+#endif
+ }
+ {
+ const char input[] = "+nan(";
+ char *ptr1;
+ char *ptr2;
+ float result1;
+ float result2;
+ errno = 0;
+ result1 = strtof (input, &ptr1);
+ result2 = strtof (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs. */
+ ASSERT (isnanf (result1)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (isnanf (result2)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (!!signbit (result1) == !!signbit (result2));
+ ASSERT (ptr1 == input + 4); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 2.5.1, mingw */
+ ASSERT (ptr2 == input + 4); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 2.5.1, mingw */
+ ASSERT (errno == 0);
+#else
+ ASSERT (result1 == 0.0f);
+ ASSERT (result2 == 0.0f);
+ ASSERT (!signbit (result1));
+ ASSERT (!signbit (result2));
+ ASSERT (ptr1 == input);
+ ASSERT (ptr2 == input + 1);
+ ASSERT (errno == 0 || errno == EINVAL);
+#endif
+ }
+ {
+ const char input[] = "-nan()";
+ char *ptr1;
+ char *ptr2;
+ float result1;
+ float result2;
+ errno = 0;
+ result1 = strtof (input, &ptr1);
+ result2 = strtof (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs. */
+ ASSERT (isnanf (result1)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (isnanf (result2)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+# if 0
+ /* Sign bits of NaN is a portability sticking point, not worth
+ worrying about. */
+ ASSERT (!!signbit (result1) != !!signbit (result2)); /* glibc-2.3.6, IRIX 6.5, OSF/1 5.1, mingw */
+# endif
+ ASSERT (ptr1 == input + 6); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr2 == input + 6); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (errno == 0);
+#else
+ ASSERT (result1 == 0.0f);
+ ASSERT (result2 == 0.0f);
+ ASSERT (!signbit (result1));
+ ASSERT (!signbit (result2));
+ ASSERT (ptr1 == input);
+ ASSERT (ptr2 == input + 1);
+ ASSERT (errno == 0 || errno == EINVAL);
+#endif
+ }
+ {
+ const char input[] = " nan().";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+#if 1 /* All known CPUs support NaNs. */
+ ASSERT (isnanf (result)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + 6); /* glibc-2.3.6, Mac OS X 10.3, FreeBSD 6.2, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (errno == 0);
+#else
+ ASSERT (result == 0.0f);
+ ASSERT (!signbit (result));
+ ASSERT (ptr == input);
+ ASSERT (errno == 0 || errno == EINVAL);
+#endif
+ }
+ {
+ /* The behavior of nan(0) is implementation-defined, but all
+ implementations we know of which handle optional
+ n-char-sequences handle nan(0) the same as nan(). */
+ const char input[] = "-nan(0).";
+ char *ptr1;
+ char *ptr2;
+ float result1;
+ float result2;
+ errno = 0;
+ result1 = strtof (input, &ptr1);
+ result2 = strtof (input + 1, &ptr2);
+#if 1 /* All known CPUs support NaNs. */
+ ASSERT (isnanf (result1)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (isnanf (result2)); /* OpenBSD 4.0, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+# if 0
+ /* Sign bits of NaN is a portability sticking point, not worth
+ worrying about. */
+ ASSERT (!!signbit (result1) != !!signbit (result2)); /* glibc-2.3.6, IRIX 6.5, OSF/1 5.1, mingw */
+# endif
+ ASSERT (ptr1 == input + 7); /* glibc-2.3.6, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr2 == input + 7); /* glibc-2.3.6, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (errno == 0);
+#else
+ ASSERT (result1 == 0.0f);
+ ASSERT (result2 == 0.0f);
+ ASSERT (!signbit (result1));
+ ASSERT (!signbit (result2));
+ ASSERT (ptr1 == input);
+ ASSERT (ptr2 == input + 1);
+ ASSERT (errno == 0 || errno == EINVAL);
+#endif
+ }
+
+ /* Hex. */
+ {
+ const char input[] = "0xa";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 10.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0XA";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 10.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1p";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1p+";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1P+";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1p+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 2.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 6); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0X1P+1";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 2.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 6); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1p+1a";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 2.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 6); /* NetBSD 3.0, OpenBSD 4.0, AIX 5.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "0x1p 2";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (ptr == input + 3); /* NetBSD 3.0, OpenBSD 4.0, AIX 7.1, HP-UX 11.11, IRIX 6.5, OSF/1 5.1, Solaris 10, mingw */
+ ASSERT (errno == 0);
+ }
+
+ /* Large buffers. */
+ {
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ float result;
+ memset (input, '\t', m - 1);
+ input[m - 1] = '1';
+ input[m] = '\0';
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+ {
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ float result;
+ memset (input, '0', m - 1);
+ input[m - 1] = '1';
+ input[m] = '\0';
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+#if 0
+ /* Newlib has an artificial limit of 20000 for the exponent. TODO -
+ gnulib should fix this. */
+ {
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ float result;
+ input[0] = '.';
+ memset (input + 1, '0', m - 10);
+ input[m - 9] = '1';
+ input[m - 8] = 'e';
+ input[m - 7] = '+';
+ input[m - 6] = '9';
+ input[m - 5] = '9';
+ input[m - 4] = '9';
+ input[m - 3] = '9';
+ input[m - 2] = '9';
+ input[m - 1] = '1';
+ input[m] = '\0';
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* Mac OS X 10.3, FreeBSD 6.2, NetBSD 3.0, OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + m); /* OSF/1 5.1 */
+ ASSERT (errno == 0); /* Mac OS X 10.3, FreeBSD 6.2, NetBSD 3.0, OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ }
+ free (input);
+ }
+ {
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ float result;
+ input[0] = '1';
+ memset (input + 1, '0', m - 9);
+ input[m - 8] = 'e';
+ input[m - 7] = '-';
+ input[m - 6] = '9';
+ input[m - 5] = '9';
+ input[m - 4] = '9';
+ input[m - 3] = '9';
+ input[m - 2] = '9';
+ input[m - 1] = '1';
+ input[m] = '\0';
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f); /* Mac OS X 10.3, FreeBSD 6.2, NetBSD 3.0, OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0); /* Mac OS X 10.3, FreeBSD 6.2, NetBSD 3.0, OpenBSD 4.0, IRIX 6.5, OSF/1 5.1, mingw */
+ }
+ free (input);
+ }
+#endif
+ {
+ size_t m = 1000000;
+ char *input = malloc (m + 1);
+ if (input)
+ {
+ char *ptr;
+ float result;
+ input[0] = '-';
+ input[1] = '0';
+ input[2] = 'e';
+ input[3] = '1';
+ memset (input + 4, '0', m - 3);
+ input[m] = '\0';
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.0f);
+ ASSERT (!!signbit (result) == !!signbit (minus_zerof)); /* IRIX 6.5, OSF/1 4.0 */
+ ASSERT (ptr == input + m);
+ ASSERT (errno == 0);
+ }
+ free (input);
+ }
+
+ /* Rounding. */
+ /* TODO - is it worth some tests of rounding for typical IEEE corner
+ cases, such as .5 ULP rounding up to the smallest denormal and
+ not causing underflow, or FLT_MIN - .5 ULP not causing an
+ infinite loop? */
+
+ return status;
+}
diff --git a/tests/test-strtof1.c b/tests/test-strtof1.c
new file mode 100644
index 0000000000..bde446405a
--- /dev/null
+++ b/tests/test-strtof1.c
@@ -0,0 +1,101 @@
+/* Test of strtof() in a French locale.
+ Copyright (C) 2019-2024 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 <https://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <errno.h>
+#include <locale.h>
+
+#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;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.0f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = ",5";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 0.5f);
+ ASSERT (ptr == input + 2);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1,5";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result == 1.5f);
+ ASSERT (ptr == input + 3);
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "1.5";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ /* On AIX 7.2, in the French locale, '.' is recognized as an alternate
+ radix character. */
+ ASSERT ((ptr == input + 1 && result == 1.0f)
+ || (ptr == input + 3 && result == 1.5f));
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "123.456,789";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ /* On AIX 7.2, in the French locale, '.' is recognized as an alternate
+ radix character. */
+ ASSERT ((ptr == input + 3 && result == 123.0f)
+ || (ptr == input + 7 && result > 123.45f && result < 123.46f));
+ ASSERT (errno == 0);
+ }
+ {
+ const char input[] = "123,456.789";
+ char *ptr;
+ float result;
+ errno = 0;
+ result = strtof (input, &ptr);
+ ASSERT (result > 123.45f && result < 123.46f);
+ ASSERT (ptr == input + 7);
+ ASSERT (errno == 0);
+ }
+
+ return 0;
+}
diff --git a/tests/test-strtof1.sh b/tests/test-strtof1.sh
new file mode 100755
index 0000000000..14592a8ed6
--- /dev/null
+++ b/tests/test-strtof1.sh
@@ -0,0 +1,30 @@
+#!/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 $LC_NUMERIC_IMPLEMENTED; then
+ :
+else
+ echo "Skipping test: LC_NUMERIC category of locales is not implemented"
+ exit 77
+fi
+
+if test $LOCALE_FR != none; then
+ LC_ALL=$LOCALE_FR ${CHECKER} ./test-strtof1${EXEEXT} || exit 1
+fi
+
+if test $LOCALE_FR_UTF8 != none; then
+ LC_ALL=$LOCALE_FR_UTF8 ${CHECKER} ./test-strtof1${EXEEXT} || exit 1
+fi
+
+exit 0
--
2.34.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2024-02-22 0:59 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-22 0:58 new module 'strtof' Bruno Haible
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).