This is the mail archive of the
newlib@sourceware.org
mailing list for the newlib project.
[PATCH] Set errno in strtof variants when conversion from double overflows
- From: Kyrill Tkachov <kyrylo dot tkachov at foss dot arm dot com>
- To: newlib at sourceware dot org
- Date: Thu, 15 Dec 2016 16:47:25 +0000
- Subject: [PATCH] Set errno in strtof variants when conversion from double overflows
- Authentication-results: sourceware.org; auth=none
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;
}