aboutsummaryrefslogtreecommitdiff
path: root/REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c
diff options
context:
space:
mode:
Diffstat (limited to 'REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c')
-rw-r--r--REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c b/REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c
new file mode 100644
index 0000000000..1f3fa1ea1e
--- /dev/null
+++ b/REORG.TODO/sysdeps/ieee754/ldbl-96/s_fmal.c
@@ -0,0 +1,296 @@
+/* Compute x * y + z as ternary operation.
+ Copyright (C) 2010-2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Jakub Jelinek <jakub@redhat.com>, 2010.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <float.h>
+#include <math.h>
+#include <fenv.h>
+#include <ieee754.h>
+#include <math_private.h>
+#include <tininess.h>
+
+/* This implementation uses rounding to odd to avoid problems with
+ double rounding. See a paper by Boldo and Melquiond:
+ http://www.lri.fr/~melquion/doc/08-tc.pdf */
+
+long double
+__fmal (long double x, long double y, long double z)
+{
+ union ieee854_long_double u, v, w;
+ int adjust = 0;
+ u.d = x;
+ v.d = y;
+ w.d = z;
+ if (__builtin_expect (u.ieee.exponent + v.ieee.exponent
+ >= 0x7fff + IEEE854_LONG_DOUBLE_BIAS
+ - LDBL_MANT_DIG, 0)
+ || __builtin_expect (u.ieee.exponent >= 0x7fff - LDBL_MANT_DIG, 0)
+ || __builtin_expect (v.ieee.exponent >= 0x7fff - LDBL_MANT_DIG, 0)
+ || __builtin_expect (w.ieee.exponent >= 0x7fff - LDBL_MANT_DIG, 0)
+ || __builtin_expect (u.ieee.exponent + v.ieee.exponent
+ <= IEEE854_LONG_DOUBLE_BIAS + LDBL_MANT_DIG, 0))
+ {
+ /* If z is Inf, but x and y are finite, the result should be
+ z rather than NaN. */
+ if (w.ieee.exponent == 0x7fff
+ && u.ieee.exponent != 0x7fff
+ && v.ieee.exponent != 0x7fff)
+ return (z + x) + y;
+ /* If z is zero and x are y are nonzero, compute the result
+ as x * y to avoid the wrong sign of a zero result if x * y
+ underflows to 0. */
+ if (z == 0 && x != 0 && y != 0)
+ return x * y;
+ /* If x or y or z is Inf/NaN, or if x * y is zero, compute as
+ x * y + z. */
+ if (u.ieee.exponent == 0x7fff
+ || v.ieee.exponent == 0x7fff
+ || w.ieee.exponent == 0x7fff
+ || x == 0
+ || y == 0)
+ return x * y + z;
+ /* If fma will certainly overflow, compute as x * y. */
+ if (u.ieee.exponent + v.ieee.exponent
+ > 0x7fff + IEEE854_LONG_DOUBLE_BIAS)
+ return x * y;
+ /* If x * y is less than 1/4 of LDBL_TRUE_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)))
+ {
+ long double force_underflow = x * y;
+ math_force_eval (force_underflow);
+ }
+ return v.d * 0x1p-65L;
+ }
+ if (u.ieee.exponent + v.ieee.exponent
+ >= 0x7fff + IEEE854_LONG_DOUBLE_BIAS - LDBL_MANT_DIG)
+ {
+ /* Compute 1p-64 times smaller result and multiply
+ at the end. */
+ if (u.ieee.exponent > v.ieee.exponent)
+ u.ieee.exponent -= LDBL_MANT_DIG;
+ else
+ v.ieee.exponent -= LDBL_MANT_DIG;
+ /* If x + y exponent is very large and z exponent is very small,
+ it doesn't matter if we don't adjust it. */
+ if (w.ieee.exponent > LDBL_MANT_DIG)
+ w.ieee.exponent -= LDBL_MANT_DIG;
+ adjust = 1;
+ }
+ else if (w.ieee.exponent >= 0x7fff - LDBL_MANT_DIG)
+ {
+ /* Similarly.
+ If z exponent is very large and x and y exponents are
+ very small, adjust them up to avoid spurious underflows,
+ rather than down. */
+ if (u.ieee.exponent + v.ieee.exponent
+ <= IEEE854_LONG_DOUBLE_BIAS + 2 * LDBL_MANT_DIG)
+ {
+ if (u.ieee.exponent > v.ieee.exponent)
+ u.ieee.exponent += 2 * LDBL_MANT_DIG + 2;
+ else
+ v.ieee.exponent += 2 * LDBL_MANT_DIG + 2;
+ }
+ else if (u.ieee.exponent > v.ieee.exponent)
+ {
+ if (u.ieee.exponent > LDBL_MANT_DIG)
+ u.ieee.exponent -= LDBL_MANT_DIG;
+ }
+ else if (v.ieee.exponent > LDBL_MANT_DIG)
+ v.ieee.exponent -= LDBL_MANT_DIG;
+ w.ieee.exponent -= LDBL_MANT_DIG;
+ adjust = 1;
+ }
+ else if (u.ieee.exponent >= 0x7fff - LDBL_MANT_DIG)
+ {
+ u.ieee.exponent -= LDBL_MANT_DIG;
+ if (v.ieee.exponent)
+ v.ieee.exponent += LDBL_MANT_DIG;
+ else
+ v.d *= 0x1p64L;
+ }
+ else if (v.ieee.exponent >= 0x7fff - LDBL_MANT_DIG)
+ {
+ v.ieee.exponent -= LDBL_MANT_DIG;
+ if (u.ieee.exponent)
+ u.ieee.exponent += LDBL_MANT_DIG;
+ else
+ u.d *= 0x1p64L;
+ }
+ else /* if (u.ieee.exponent + v.ieee.exponent
+ <= IEEE854_LONG_DOUBLE_BIAS + LDBL_MANT_DIG) */
+ {
+ if (u.ieee.exponent > v.ieee.exponent)
+ u.ieee.exponent += 2 * LDBL_MANT_DIG + 2;
+ else
+ v.ieee.exponent += 2 * LDBL_MANT_DIG + 2;
+ if (w.ieee.exponent <= 4 * LDBL_MANT_DIG + 6)
+ {
+ if (w.ieee.exponent)
+ w.ieee.exponent += 2 * LDBL_MANT_DIG + 2;
+ else
+ w.d *= 0x1p130L;
+ adjust = -1;
+ }
+ /* Otherwise x * y should just affect inexact
+ and nothing else. */
+ }
+ x = u.d;
+ y = v.d;
+ z = w.d;
+ }
+
+ /* Ensure correct sign of exact 0 + 0. */
+ if (__glibc_unlikely ((x == 0 || y == 0) && z == 0))
+ {
+ x = math_opt_barrier (x);
+ return x * y + z;
+ }
+
+ fenv_t env;
+ feholdexcept (&env);
+ fesetround (FE_TONEAREST);
+
+ /* Multiplication m1 + m2 = x * y using Dekker's algorithm. */
+#define C ((1LL << (LDBL_MANT_DIG + 1) / 2) + 1)
+ long double x1 = x * C;
+ long double y1 = y * C;
+ long double m1 = x * y;
+ x1 = (x - x1) + x1;
+ y1 = (y - y1) + y1;
+ long double x2 = x - x1;
+ long double y2 = y - y1;
+ long double m2 = (((x1 * y1 - m1) + x1 * y2) + x2 * y1) + x2 * y2;
+
+ /* Addition a1 + a2 = z + m1 using Knuth's algorithm. */
+ long double a1 = z + m1;
+ long double t1 = a1 - z;
+ long double t2 = a1 - t1;
+ t1 = m1 - t1;
+ t2 = z - t2;
+ long double a2 = t1 + t2;
+ /* Ensure the arithmetic is not scheduled after feclearexcept call. */
+ math_force_eval (m2);
+ math_force_eval (a2);
+ feclearexcept (FE_INEXACT);
+
+ /* If the result is an exact zero, ensure it has the correct sign. */
+ if (a1 == 0 && m2 == 0)
+ {
+ feupdateenv (&env);
+ /* Ensure that round-to-nearest value of z + m1 is not reused. */
+ z = math_opt_barrier (z);
+ return z + m1;
+ }
+
+ fesetround (FE_TOWARDZERO);
+ /* Perform m2 + a2 addition with round to odd. */
+ u.d = a2 + m2;
+
+ if (__glibc_likely (adjust == 0))
+ {
+ if ((u.ieee.mantissa1 & 1) == 0 && u.ieee.exponent != 0x7fff)
+ u.ieee.mantissa1 |= fetestexcept (FE_INEXACT) != 0;
+ feupdateenv (&env);
+ /* Result is a1 + u.d. */
+ return a1 + u.d;
+ }
+ else if (__glibc_likely (adjust > 0))
+ {
+ if ((u.ieee.mantissa1 & 1) == 0 && u.ieee.exponent != 0x7fff)
+ u.ieee.mantissa1 |= fetestexcept (FE_INEXACT) != 0;
+ feupdateenv (&env);
+ /* Result is a1 + u.d, scaled up. */
+ return (a1 + u.d) * 0x1p64L;
+ }
+ else
+ {
+ if ((u.ieee.mantissa1 & 1) == 0)
+ u.ieee.mantissa1 |= fetestexcept (FE_INEXACT) != 0;
+ v.d = a1 + u.d;
+ /* Ensure the addition is not scheduled after fetestexcept call. */
+ math_force_eval (v.d);
+ int j = fetestexcept (FE_INEXACT) != 0;
+ feupdateenv (&env);
+ /* Ensure the following computations are performed in default rounding
+ mode instead of just reusing the round to zero computation. */
+ asm volatile ("" : "=m" (u) : "m" (u));
+ /* If a1 + u.d is exact, the only rounding happens during
+ scaling down. */
+ if (j == 0)
+ return v.d * 0x1p-130L;
+ /* If result rounded to zero is not subnormal, no double
+ rounding will occur. */
+ if (v.ieee.exponent > 130)
+ return (a1 + u.d) * 0x1p-130L;
+ /* If v.d * 0x1p-130L with round to zero is a subnormal above
+ or equal to LDBL_MIN / 2, then v.d * 0x1p-130L shifts mantissa
+ down just by 1 bit, which means v.ieee.mantissa1 |= j would
+ change the round bit, not sticky or guard bit.
+ v.d * 0x1p-130L never normalizes by shifting up,
+ so round bit plus sticky bit should be already enough
+ for proper rounding. */
+ if (v.ieee.exponent == 130)
+ {
+ /* If the exponent would be in the normal range when
+ rounding to normal precision with unbounded exponent
+ range, the exact result is known and spurious underflows
+ must be avoided on systems detecting tininess after
+ rounding. */
+ if (TININESS_AFTER_ROUNDING)
+ {
+ w.d = a1 + u.d;
+ if (w.ieee.exponent == 131)
+ return w.d * 0x1p-130L;
+ }
+ /* v.ieee.mantissa1 & 2 is LSB bit of the result before rounding,
+ v.ieee.mantissa1 & 1 is the round bit and j is our sticky
+ bit. */
+ w.d = 0.0L;
+ w.ieee.mantissa1 = ((v.ieee.mantissa1 & 3) << 1) | j;
+ w.ieee.negative = v.ieee.negative;
+ v.ieee.mantissa1 &= ~3U;
+ v.d *= 0x1p-130L;
+ w.d *= 0x1p-2L;
+ return v.d + w.d;
+ }
+ v.ieee.mantissa1 |= j;
+ return v.d * 0x1p-130L;
+ }
+}
+weak_alias (__fmal, fmal)