aboutsummaryrefslogtreecommitdiff
path: root/newlib/libc/stdlib/strtod.c
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/stdlib/strtod.c')
-rw-r--r--newlib/libc/stdlib/strtod.c31
1 files changed, 29 insertions, 2 deletions
diff --git a/newlib/libc/stdlib/strtod.c b/newlib/libc/stdlib/strtod.c
index 8bb75ef..019416c 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;