The gdtoa implementation uses the type long, defined as Long, in lots
of code. For historical reason newlib defines Long as int32_t instead.
This works fine, as long as floating point exceptions are not enabled.
The conversion to 32 bit int can lead to a FE_INVALID situation.
Example:
const char *str = "
121645100408832000.0";
char *ptr;
feenableexcept (FE_INVALID);
strtod (str, &ptr);
This leads to the following situation in strtod
double aadj;
Long L;
[...]
L = (Long)aadj;
For instance, on x86_64 the code here is
cvttsd2si %xmm0,%eax
At this point, aadj is
2529648000.0 in our example. The conversion to
32 bit %eax results in a negative int value, thus the conversion is
invalid. With feenableexcept (FE_INVALID), a SIGFPE is raised.
Fix this by always using 64 bit ints here if double is not a 32 bit type
to avoid this type of FP exceptions.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
#endif
if (y == z) {
/* Can we stop now? */
+#ifndef _DOUBLE_IS_32BITS
+ /* If FE_INVALID floating point exceptions are
+ enabled, a conversion to a 32 bit value is
+ dangerous. A positive double value can result
+ in a negative 32 bit int, thus raising SIGFPE.
+ To avoid this, always convert into 64 bit here. */
+ __int64_t L = (__int64_t)aadj;
+#else
L = (Long)aadj;
+#endif
aadj -= L;
/* The tolerances below are conservative. */
if (dsign || dword1(rv) || dword0(rv) & Bndry_mask) {