Re: strtold does not set errno when it should

Bruno Haible <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <3071059.SjtNWNUZ6R@omega>
Hi Corinna,

> The code for strtold is almost verbatim taken from
> 
>   https://github.com/jwiegley/gdtoa.git
> 
> There haven't been any patches upstream.

Probably that's because the code is good enough for ISO C compliance;
however, POSIX [1] adds the "ERANGE upon underflow" requirement,
whereas ISO C only has an "ERANGE upon overflow" requirement.

[1] <https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtod.html>

> diff --git a/newlib/libc/stdlib/strtodg.c b/newlib/libc/stdlib/strtodg.c
> index 013315946c1b..d6fb26ad3b45 100644
> --- a/newlib/libc/stdlib/strtodg.c
> +++ b/newlib/libc/stdlib/strtodg.c
> @@ -1091,6 +1091,10 @@ _strtodg_l (struct _reent *p, const char *s00, char **se, FPI *fpi, Long *exp,
>  				irv |= STRTOG_Underflow;
>  			}
>  		}
> +#ifndef NO_ERRNO
> +	if (irv & STRTOG_Underflow)
> +		errno = ERANGE;
> +#endif
>  	if (se)
>  		*se = (char *)s;
>  	if (sign)
> 
> which seems to do the trick.  But, does it make sense?  If not, I'd
> really appreciate a patch.

I think it goes in the right direction.

It can be improved a bit, though: The existing code for overflow is careful
to do the errno stuff only when STRTOG_Overflow is being set/added. I.e.
keep this overflow/underflow handling out of the main path, in order not
to slow down the 99.99% of the cases that don't need it.

My attempt to do this would look like the attached patch. Untested.

Bruno
strtodg.c.diff (text/x-patch, 1.9 KB)
diff --git a/newlib/libc/stdlib/strtodg.c b/newlib/libc/stdlib/strtodg.c
index 0133159..743f60b 100644
--- a/newlib/libc/stdlib/strtodg.c
+++ b/newlib/libc/stdlib/strtodg.c
@@ -348,6 +348,9 @@ rvOK (struct _reent *p, double d, FPI *fpi, Long *exp, __ULong *bits, int exact,
 		if (k > nb || fpi->sudden_underflow) {
 			b->_wds = inex = 0;
 			*irv = STRTOG_Underflow | STRTOG_Inexlo;
+#ifndef NO_ERRNO
+			errno = ERANGE;
+#endif
 			}
 		else {
 			k1 = k - 1;
@@ -362,9 +365,15 @@ rvOK (struct _reent *p, double d, FPI *fpi, Long *exp, __ULong *bits, int exact,
 			if (carry) {
 				b = increment(p, b);
 				inex = STRTOG_Inexhi | STRTOG_Underflow;
+#ifndef NO_ERRNO
+				errno = ERANGE;
+#endif
 				}
 			else if (lostbits)
 				inex = STRTOG_Inexlo | STRTOG_Underflow;
+#ifndef NO_ERRNO
+				errno = ERANGE;
+#endif
 			}
 		}
 	else if (e > fpi->emax) {
@@ -761,6 +770,9 @@ _strtodg_l (struct _reent *p, const char *s00, char **se, FPI *fpi, Long *exp,
 					rvb->_x[0] = 0;
 					*exp = emin;
 					irv = STRTOG_Underflow | STRTOG_Inexlo;
+#ifndef NO_ERRNO
+					errno = ERANGE;
+#endif
 					goto ret;
 					}
 				rvb->_x[0] = rvb->_wds = rvbits = 1;
@@ -940,6 +952,9 @@ _strtodg_l (struct _reent *p, const char *s00, char **se, FPI *fpi, Long *exp,
 				rvb->_wds = 0;
 				rve = emin;
 				irv = STRTOG_Underflow | STRTOG_Inexlo;
+#ifndef NO_ERRNO
+				errno = ERANGE;
+#endif
 				break;
 				}
 			adj0 = dval(adj) = 1.;
@@ -1083,12 +1098,18 @@ _strtodg_l (struct _reent *p, const char *s00, char **se, FPI *fpi, Long *exp,
 		if (sudden_underflow) {
 			rvb->_wds = 0;
 			irv = STRTOG_Underflow | STRTOG_Inexlo;
+#ifndef NO_ERRNO
+			errno = ERANGE;
+#endif
 			}
 		else  {
 			irv = (irv & ~STRTOG_Retmask) |
 				(rvb->_wds > 0 ? STRTOG_Denormal : STRTOG_Zero);
 			if (irv & STRTOG_Inexact)
 				irv |= STRTOG_Underflow;
+#ifndef NO_ERRNO
+				errno = ERANGE;
+#endif
 			}
 		}
 	if (se)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.