summaryrefslogtreecommitdiff
path: root/sysdeps/ieee754/ldbl-96/s_fmal.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2012-11-01 16:47:26 +0000
committerJoseph Myers <joseph@codesourcery.com>2012-11-01 16:47:26 +0000
commit473611b22d630a5cbb870d2a7edf08071d4d8ace (patch)
tree072d5b0e02816731a992290d057ce6373d4d3e24 /sysdeps/ieee754/ldbl-96/s_fmal.c
parent4078da3d4ed94e148986b50f4fbaf9cca87fc52c (diff)
Fix fma (a, b, c) for small a * b (bugs 14784, 14785).
Diffstat (limited to 'sysdeps/ieee754/ldbl-96/s_fmal.c')
-rw-r--r--sysdeps/ieee754/ldbl-96/s_fmal.c36
1 files changed, 32 insertions, 4 deletions
diff --git a/sysdeps/ieee754/ldbl-96/s_fmal.c b/sysdeps/ieee754/ldbl-96/s_fmal.c
index 91333af353..b5fdcba6bc 100644
--- a/sysdeps/ieee754/ldbl-96/s_fmal.c
+++ b/sysdeps/ieee754/ldbl-96/s_fmal.c
@@ -57,16 +57,44 @@ __fmal (long double x, long double y, long double z)
if (z == 0 && x != 0 && y != 0)
return x * y;
/* If x or y or z is Inf/NaN, or if fma will certainly overflow,
- or if x * y is less than half of LDBL_DENORM_MIN,
- compute as x * y + z. */
+ or if x * y is zero, compute as x * y + z. */
if (u.ieee.exponent == 0x7fff
|| v.ieee.exponent == 0x7fff
|| w.ieee.exponent == 0x7fff
|| u.ieee.exponent + v.ieee.exponent
> 0x7fff + IEEE854_LONG_DOUBLE_BIAS
- || u.ieee.exponent + v.ieee.exponent
- < IEEE854_LONG_DOUBLE_BIAS - LDBL_MANT_DIG - 2)
+ || x == 0
+ || y == 0)
return x * y + z;
+ /* If x * y is less than 1/4 of LDBL_DENORM_MIN, neither the
+ result nor whether there is underflow depends on its exact
+ value, only on its sign. */
+ if (u.ieee.exponent + v.ieee.exponent
+ < IEEE854_LONG_DOUBLE_BIAS - LDBL_MANT_DIG - 2)
+ {
+ int neg = u.ieee.negative ^ v.ieee.negative;
+ long double tiny = neg ? -0x1p-16445L : 0x1p-16445L;
+ if (w.ieee.exponent >= 3)
+ return tiny + z;
+ /* Scaling up, adding TINY and scaling down produces the
+ correct result, because in round-to-nearest mode adding
+ TINY has no effect and in other modes double rounding is
+ harmless. But it may not produce required underflow
+ exceptions. */
+ v.d = z * 0x1p65L + tiny;
+ if (TININESS_AFTER_ROUNDING
+ ? v.ieee.exponent < 66
+ : (w.ieee.exponent == 0
+ || (w.ieee.exponent == 1
+ && w.ieee.negative != neg
+ && w.ieee.mantissa1 == 0
+ && w.ieee.mantissa0 == 0x80000000)))
+ {
+ volatile long double force_underflow = x * y;
+ (void) force_underflow;
+ }
+ return v.d * 0x1p-65L;
+ }
if (u.ieee.exponent + v.ieee.exponent
>= 0x7fff + IEEE854_LONG_DOUBLE_BIAS - LDBL_MANT_DIG)
{