diff options
author | Joseph Myers <joseph@codesourcery.com> | 2012-10-30 13:51:27 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2012-10-30 13:51:27 +0000 |
commit | 2a27fd6dae3edec949deda9a55928a0e22c8a8ae (patch) | |
tree | 9c9fcc6c86b0f7c04454a5833d5efbe17dc79117 /stdlib/strtod_l.c | |
parent | e5088dc6870b072a263f207af9e410c82f80a09e (diff) | |
download | glibc-2a27fd6dae3edec949deda9a55928a0e22c8a8ae.tar.gz glibc-2a27fd6dae3edec949deda9a55928a0e22c8a8ae.tar.xz glibc-2a27fd6dae3edec949deda9a55928a0e22c8a8ae.zip |
Fix strtod handling of underflow (bug 14047).
Diffstat (limited to 'stdlib/strtod_l.c')
-rw-r--r-- | stdlib/strtod_l.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/stdlib/strtod_l.c b/stdlib/strtod_l.c index 95f13e40a2..fdce35742a 100644 --- a/stdlib/strtod_l.c +++ b/stdlib/strtod_l.c @@ -62,6 +62,7 @@ extern unsigned long long int ____strtoull_l_internal (const char *, char **, #include <string.h> #include <stdint.h> #include <rounding-mode.h> +#include <tininess.h> /* The gmp headers need some configuration frobs. */ #define HAVE_ALLOCA 1 @@ -209,12 +210,15 @@ static FLOAT round_and_return (mp_limb_t *retval, intmax_t exponent, int negative, mp_limb_t round_limb, mp_size_t round_bit, int more_bits) { + int mode = get_rounding_mode (); + if (exponent < MIN_EXP - 1) { if (exponent < MIN_EXP - 1 - MANT_DIG) return underflow_value (negative); mp_size_t shift = MIN_EXP - 1 - exponent; + bool is_tiny = true; more_bits |= (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0; if (shift == MANT_DIG) @@ -248,6 +252,33 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative, } else if (shift > 0) { + if (TININESS_AFTER_ROUNDING && shift == 1) + { + /* Whether the result counts as tiny depends on whether, + after rounding to the normal precision, it still has + a subnormal exponent. */ + mp_limb_t retval_normal[RETURN_LIMB_SIZE]; + if (round_away (negative, + (retval[0] & 1) != 0, + (round_limb + & (((mp_limb_t) 1) << round_bit)) != 0, + (more_bits + || ((round_limb + & ((((mp_limb_t) 1) << round_bit) - 1)) + != 0)), + mode)) + { + mp_limb_t cy = __mpn_add_1 (retval_normal, retval, + RETURN_LIMB_SIZE, 1); + + if (((MANT_DIG % BITS_PER_MP_LIMB) == 0 && cy) || + ((MANT_DIG % BITS_PER_MP_LIMB) != 0 && + ((retval_normal[RETURN_LIMB_SIZE - 1] + & (((mp_limb_t) 1) << (MANT_DIG % BITS_PER_MP_LIMB))) + != 0))) + is_tiny = false; + } + } round_limb = retval[0]; round_bit = shift - 1; (void) __mpn_rshift (retval, retval, RETURN_LIMB_SIZE, shift); @@ -259,14 +290,20 @@ round_and_return (mp_limb_t *retval, intmax_t exponent, int negative, # define DENORM_EXP (MIN_EXP - 2) #endif exponent = DENORM_EXP; - __set_errno (ERANGE); + if (is_tiny + && ((round_limb & (((mp_limb_t) 1) << round_bit)) != 0 + || more_bits + || (round_limb & ((((mp_limb_t) 1) << round_bit) - 1)) != 0)) + { + __set_errno (ERANGE); + volatile FLOAT force_underflow_exception = MIN_VALUE * MIN_VALUE; + (void) force_underflow_exception; + } } if (exponent > MAX_EXP) goto overflow; - int mode = get_rounding_mode (); - if (round_away (negative, (retval[0] & 1) != 0, (round_limb & (((mp_limb_t) 1) << round_bit)) != 0, |