[PATCH] stdlib: Make strtod/strtof set ERANGE consistently for underflow.

Keith Packard <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
(I suspect this patch is incomplete, but it covers all of the test cases
I've got. Help identifying additional places where this bug might exist
would be greatly appreciated)

The C standard says that errno may acquire the value ERANGE if the
result from strtod underflows. According to IEEE 754, underflow occurs
whenever the value cannot be represented in normalized form.

Newlib is inconsistent in this, setting errno to ERANGE only if the
value underflows to zero, but not for denorm values, and never for hex
format floats.

This patch attempts to consistently set errno to ERANGE for all
'underflow' conditions, which is to say all values which are not
exactly zero and which cannot be represented in normalized form.

This matches glibc behavior, as well as the Linux, Mac OS X, OpenBSD,
FreeBSD and SunOS strtod man pages.


-- 
-keith
0001-stdlib-Make-strtod-strtof-set-ERANGE-consistently-fo.patch (text/x-diff, 3 KB)
From 3464e947eb12dd27c11fd88ef6a75ad0e560542d Mon Sep 17 00:00:00 2001
From: Keith Packard <[email protected]>
Date: Tue, 22 Jun 2021 10:26:26 -0700
Subject: [PATCH] stdlib: Make strtod/strtof set ERANGE consistently for
 underflow.

The C standard says that errno may acquire the value ERANGE if the
result from strtod underflows. According to IEEE 754, underflow occurs
whenever the value cannot be represented in normalized form.

Newlib is inconsistent in this, setting errno to ERANGE only if the
value underflows to zero, but not for denorm values, and never for hex
format floats.

This patch attempts to consistently set errno to ERANGE for all
'underflow' conditions, which is to say all values which are not
exactly zero and which cannot be represented in normalized form.

This matches glibc behavior, as well as the Linux, Mac OS X, OpenBSD,
FreeBSD and SunOS strtod man pages.

Signed-off-by: Keith Packard <[email protected]>
---
 newlib/libc/stdlib/strtod.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/newlib/libc/stdlib/strtod.c b/newlib/libc/stdlib/strtod.c
index 8bb75ef0a..019416ca7 100644
--- a/newlib/libc/stdlib/strtod.c
+++ b/newlib/libc/stdlib/strtod.c
@@ -326,6 +326,11 @@ _strtod_l (struct _reent *ptr, const char *__restrict s00, char **__restrict se,
 					Bfree(ptr,bb);
 					}
 				ULtod(rv.i, bits, exp, i);
+#ifndef NO_ERRNO
+                                /* try to avoid the bug of testing an 8087 register value */
+                                if ((dword0(rv)&Exp_mask) == 0)
+                                    errno = ERANGE;
+#endif
 			  }}
 			goto ret;
 		  }
@@ -1238,7 +1243,7 @@ _strtod_l (struct _reent *ptr, const char *__restrict s00, char **__restrict se,
 		dval(rv) *= dval(rv0);
 #ifndef NO_ERRNO
 		/* try to avoid the bug of testing an 8087 register value */
-		if (dword0(rv) == 0 && dword1(rv) == 0)
+		if ((dword0(rv) & Exp_mask) == 0)
 			ptr->_errno = ERANGE;
 #endif
 		}
@@ -1298,6 +1303,28 @@ strtof_l (const char *__restrict s00, char **__restrict se, locale_t loc)
   return retval;
 }
 
+/*
+ * These two functions are not quite correct as they return true for
+ * zero, however they are 'good enough' for the test in strtof below
+ * as we only need to know whether the double test is false when
+ * the float test is true.
+ */
+static inline int
+isdenorm(double d)
+{
+    U u;
+    dval(u) = d;
+    return (dword0(u) & Exp_mask) == 0;
+}
+
+static inline int
+isdenormf(float f)
+{
+    union { float f; __uint32_t i; } u;
+    u.f = f;
+    return (u.i & 0x7f800000) == 0;
+}
+
 float
 strtof (const char *__restrict s00,
 	char **__restrict se)
@@ -1307,7 +1334,7 @@ strtof (const char *__restrict s00,
     return signbit (val) ? -nanf ("") : nanf ("");
   float retval = (float) val;
 #ifndef NO_ERRNO
-  if (isinf (retval) && !isinf (val))
+  if ((isinf (retval) && !isinf (val)) || (isdenormf(retval) && !isdenorm(val)))
     _REENT->_errno = ERANGE;
 #endif
   return retval;
-- 
2.32.0
signature.asc (application/pgp-signature, 832 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEw4O3eCVWE9/bQJ2R2yIaaQAAABEFAmDSIoEACgkQ2yIaaQAA
ABFmtBAAj9Si/AvFSvKWXdIfEBuQ1X+P0U85zwC25tW2KpGYOp58mYM95/yvY6XA
CrZKEBdlephccX38CLKXDuu0C0Fv33EhdLlFhLfB1FTiOL3GdJyMn6M9tciJA/vW
KtbUX93RKf3greI4KhNaXddy4S4RRJfeU7VXG+8A/3afRCbOqy8+1GxLCyM6HPiP
dgBkqHtNm5feP2FZIM4L5YWhBgXy0R5fdI2K5fpj4n5tLQ6fQxNp7kgsS781B4z5
QeAqn0XNHZz2oBvIFW+zU6lI6wdArmRIAQ0kOv5wpzoAa3gTOIJc1TJ9ywJzTHqv
4yrOpSn2iHWG8xoJWmQdukLpFmk/y0aMuPQzS+JFyEtttdhE4cnl4aFD4bwNb+2a
3GBw1Dluvfz+vI3yOXZguDKd0EAB1qDPeCaH2vtkBzH0lNXa3UwZIFqo0QSgCfn8
ZzzzkA97PPwFgBawsZEaFvph+GEyZD/9UxD9c+pOgrSzsmXy6zEKlw/o4YkaW1MN
OYXRGStzTI7JDkrVJdU7WNxdfHmTMMB0PKXrK9hfiIgJ363/tI5UnD1RvBpScqMy
71qWtVKSeFfqUkY8NwS7uRpIMp6l1mG56XonBQr23xPkEFcmxl+nz4BaUhErJVIK
+DvtiNaMhlYq9XTAiqDxTcOosU3Z8CBSoyjzFeaCdS+CM9v91Uo=
=kWtq
-----END PGP SIGNATURE-----
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.