This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Set errno in strtof variants when conversion from double overflows


Hi all,

I've been investigating some libstdc++ execution failures on newlib bare-metal targets, in particular arm-none-eabi and aarch64-none-elf.
In particular:
21_strings/basic_string/numeric_conversions/char/stof.cc
21_strings/basic_string/numeric_conversions/wchar_t/stof.cc

These tests perform a conversion from string to a float and end up mapping down to the strtof/wcstof C functions.
They create a string from a large number that is representable in double precision but not in single precision and expect
strtof to return infinity and set errno to ERANGE so that libstdc++ can throw an exception.
But the implementation of strtof uses strtod internally to convert the string to a double and then cast the result to a float.
This works for getting the infinity result but doesn't set errno properly.
This patch adds a check after the conversion to see if the cast ended up generating an infinity from a non-infinity and sets errno
in that case.

With this patch the aforementioned tests pass on arm-none-eabi and aarch64-none-elf.

Is this ok?
If so, can someone please commit it on my behalf?

Thanks,
Kyrill

2016-12-15  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>

    * libc/stdlib/strtod.c (strtof_l): Set errno to ERANGE when double to
    float conversion results in infinity.
    (strtof): Likewise.
    * libc/stdlib/wcstod.c (wcstof_l): Likewise.
    (wcstof): Likewise.
diff --git a/newlib/libc/stdlib/strtod.c b/newlib/libc/stdlib/strtod.c
index e908fcb..f82f507 100644
--- a/newlib/libc/stdlib/strtod.c
+++ b/newlib/libc/stdlib/strtod.c
@@ -1293,9 +1293,14 @@ _DEFUN (strtod, (s00, se),
 float
 strtof_l (const char *__restrict s00, char **__restrict se, locale_t loc)
 {
-  double retval = _strtod_l (_REENT, s00, se, loc);
-  if (isnan (retval))
+  double val = _strtod_l (_REENT, s00, se, loc);
+  if (isnan (val))
     return nanf (NULL);
+  float retval = (float) val;
+#ifndef NO_ERRNO
+  if (isinf (retval) && !isinf (val))
+    _REENT->_errno = ERANGE;
+#endif
   return (float)retval;
 }
 
@@ -1304,9 +1309,14 @@ _DEFUN (strtof, (s00, se),
 	_CONST char *__restrict s00 _AND
 	char **__restrict se)
 {
-  double retval = _strtod_l (_REENT, s00, se, __get_current_locale ());
-  if (isnan (retval))
+  double val = _strtod_l (_REENT, s00, se, __get_current_locale ());
+  if (isnan (val))
     return nanf (NULL);
+  float retval = (float) val;
+#ifndef NO_ERRNO
+  if (isinf (retval) && !isinf (val))
+    _REENT->_errno = ERANGE;
+#endif
   return (float)retval;
 }
 
diff --git a/newlib/libc/stdlib/wcstod.c b/newlib/libc/stdlib/wcstod.c
index 43f7b4e..9b7948e 100644
--- a/newlib/libc/stdlib/wcstod.c
+++ b/newlib/libc/stdlib/wcstod.c
@@ -274,9 +274,14 @@ float
 wcstof_l (const wchar_t *__restrict nptr, wchar_t **__restrict endptr,
 	  locale_t loc)
 {
-  double retval = _wcstod_l (_REENT, nptr, endptr, loc);
-  if (isnan (retval))
+  double val = _wcstod_l (_REENT, nptr, endptr, loc);
+  if (isnan (val))
     return nanf (NULL);
+  float retval = (float) val;
+#ifndef NO_ERRNO
+  if (isinf (retval) && !isinf (val))
+    _REENT->_errno = ERANGE;
+#endif
   return (float)retval;
 }
 
@@ -285,9 +290,15 @@ _DEFUN (wcstof, (nptr, endptr),
 	_CONST wchar_t *__restrict nptr _AND
 	wchar_t **__restrict endptr)
 {
-  double retval = _wcstod_l (_REENT, nptr, endptr, __get_current_locale ());
-  if (isnan (retval))
+  double val = _wcstod_l (_REENT, nptr, endptr, __get_current_locale ());
+  if (isnan (val))
     return nanf (NULL);
+  float retval = (float) val;
+#ifndef NO_ERRNO
+  if (isinf (retval) && !isinf (val))
+    _REENT->_errno = ERANGE;
+#endif
+
   return (float)retval;
 }
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]