Update to 4.8.2.
[platform/upstream/gcc48.git] / libquadmath / math / fmaq.c
1 /* Compute x * y + z as ternary operation.
2    Copyright (C) 2010-2012 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Jakub Jelinek <jakub@redhat.com>, 2010.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19
20 #include "quadmath-imp.h"
21 #include <math.h>
22 #include <float.h>
23 #ifdef HAVE_FENV_H
24 # include <fenv.h>
25 # if defined HAVE_FEHOLDEXCEPT && defined HAVE_FESETROUND \
26      && defined HAVE_FEUPDATEENV && defined HAVE_FETESTEXCEPT \
27      && defined FE_TOWARDZERO && defined FE_INEXACT
28 #  define USE_FENV_H
29 # endif
30 #endif
31
32 /* This implementation uses rounding to odd to avoid problems with
33    double rounding.  See a paper by Boldo and Melquiond:
34    http://www.lri.fr/~melquion/doc/08-tc.pdf  */
35
36 __float128
37 fmaq (__float128 x, __float128 y, __float128 z)
38 {
39   ieee854_float128 u, v, w;
40   int adjust = 0;
41   u.value = x;
42   v.value = y;
43   w.value = z;
44   if (__builtin_expect (u.ieee.exponent + v.ieee.exponent
45                         >= 0x7fff + IEEE854_FLOAT128_BIAS
46                            - FLT128_MANT_DIG, 0)
47       || __builtin_expect (u.ieee.exponent >= 0x7fff - FLT128_MANT_DIG, 0)
48       || __builtin_expect (v.ieee.exponent >= 0x7fff - FLT128_MANT_DIG, 0)
49       || __builtin_expect (w.ieee.exponent >= 0x7fff - FLT128_MANT_DIG, 0)
50       || __builtin_expect (u.ieee.exponent + v.ieee.exponent
51                            <= IEEE854_FLOAT128_BIAS + FLT128_MANT_DIG, 0))
52     {
53       /* If z is Inf, but x and y are finite, the result should be
54          z rather than NaN.  */
55       if (w.ieee.exponent == 0x7fff
56           && u.ieee.exponent != 0x7fff
57           && v.ieee.exponent != 0x7fff)
58         return (z + x) + y;
59       /* If z is zero and x are y are nonzero, compute the result
60          as x * y to avoid the wrong sign of a zero result if x * y
61          underflows to 0.  */
62       if (z == 0 && x != 0 && y != 0)
63         return x * y;
64       /* If x or y or z is Inf/NaN, or if x * y is zero, compute as
65          x * y + z.  */
66       if (u.ieee.exponent == 0x7fff
67           || v.ieee.exponent == 0x7fff
68           || w.ieee.exponent == 0x7fff
69           || x == 0
70           || y == 0)
71         return x * y + z;
72       /* If fma will certainly overflow, compute as x * y.  */
73       if (u.ieee.exponent + v.ieee.exponent
74           > 0x7fff + IEEE854_FLOAT128_BIAS)
75         return x * y;
76       /* If x * y is less than 1/4 of FLT128_DENORM_MIN, neither the
77          result nor whether there is underflow depends on its exact
78          value, only on its sign.  */
79       if (u.ieee.exponent + v.ieee.exponent
80           < IEEE854_FLOAT128_BIAS - FLT128_MANT_DIG - 2)
81         {
82           int neg = u.ieee.negative ^ v.ieee.negative;
83           __float128 tiny = neg ? -0x1p-16494Q : 0x1p-16494Q;
84           if (w.ieee.exponent >= 3)
85             return tiny + z;
86           /* Scaling up, adding TINY and scaling down produces the
87              correct result, because in round-to-nearest mode adding
88              TINY has no effect and in other modes double rounding is
89              harmless.  But it may not produce required underflow
90              exceptions.  */
91           v.value = z * 0x1p114Q + tiny;
92           if (TININESS_AFTER_ROUNDING
93               ? v.ieee.exponent < 115
94               : (w.ieee.exponent == 0
95                  || (w.ieee.exponent == 1
96                      && w.ieee.negative != neg
97                      && w.ieee.mant_low == 0
98                      && w.ieee.mant_high == 0)))
99             {
100               volatile __float128 force_underflow = x * y;
101               (void) force_underflow;
102             }
103           return v.value * 0x1p-114Q;
104         }
105       if (u.ieee.exponent + v.ieee.exponent
106           >= 0x7fff + IEEE854_FLOAT128_BIAS - FLT128_MANT_DIG)
107         {
108           /* Compute 1p-113 times smaller result and multiply
109              at the end.  */
110           if (u.ieee.exponent > v.ieee.exponent)
111             u.ieee.exponent -= FLT128_MANT_DIG;
112           else
113             v.ieee.exponent -= FLT128_MANT_DIG;
114           /* If x + y exponent is very large and z exponent is very small,
115              it doesn't matter if we don't adjust it.  */
116           if (w.ieee.exponent > FLT128_MANT_DIG)
117             w.ieee.exponent -= FLT128_MANT_DIG;
118           adjust = 1;
119         }
120       else if (w.ieee.exponent >= 0x7fff - FLT128_MANT_DIG)
121         {
122           /* Similarly.
123              If z exponent is very large and x and y exponents are
124              very small, adjust them up to avoid spurious underflows,
125              rather than down.  */
126           if (u.ieee.exponent + v.ieee.exponent
127               <= IEEE854_FLOAT128_BIAS + FLT128_MANT_DIG)
128             {
129               if (u.ieee.exponent > v.ieee.exponent)
130                 u.ieee.exponent += 2 * FLT128_MANT_DIG + 2;
131               else
132                 v.ieee.exponent += 2 * FLT128_MANT_DIG + 2;
133             }
134           else if (u.ieee.exponent > v.ieee.exponent)
135             {
136               if (u.ieee.exponent > FLT128_MANT_DIG)
137                 u.ieee.exponent -= FLT128_MANT_DIG;
138             }
139           else if (v.ieee.exponent > FLT128_MANT_DIG)
140             v.ieee.exponent -= FLT128_MANT_DIG;
141           w.ieee.exponent -= FLT128_MANT_DIG;
142           adjust = 1;
143         }
144       else if (u.ieee.exponent >= 0x7fff - FLT128_MANT_DIG)
145         {
146           u.ieee.exponent -= FLT128_MANT_DIG;
147           if (v.ieee.exponent)
148             v.ieee.exponent += FLT128_MANT_DIG;
149           else
150             v.value *= 0x1p113Q;
151         }
152       else if (v.ieee.exponent >= 0x7fff - FLT128_MANT_DIG)
153         {
154           v.ieee.exponent -= FLT128_MANT_DIG;
155           if (u.ieee.exponent)
156             u.ieee.exponent += FLT128_MANT_DIG;
157           else
158             u.value *= 0x1p113Q;
159         }
160       else /* if (u.ieee.exponent + v.ieee.exponent
161                   <= IEEE854_FLOAT128_BIAS + FLT128_MANT_DIG) */
162         {
163           if (u.ieee.exponent > v.ieee.exponent)
164             u.ieee.exponent += 2 * FLT128_MANT_DIG;
165           else
166             v.ieee.exponent += 2 * FLT128_MANT_DIG;
167           if (w.ieee.exponent <= 4 * FLT128_MANT_DIG + 4)
168             {
169               if (w.ieee.exponent)
170                 w.ieee.exponent += 2 * FLT128_MANT_DIG;
171               else
172                 w.value *= 0x1p226Q;
173               adjust = -1;
174             }
175           /* Otherwise x * y should just affect inexact
176              and nothing else.  */
177         }
178       x = u.value;
179       y = v.value;
180       z = w.value;
181     }
182
183   /* Ensure correct sign of exact 0 + 0.  */
184   if (__builtin_expect ((x == 0 || y == 0) && z == 0, 0))
185     return x * y + z;
186
187 #ifdef USE_FENV_H
188   fenv_t env;
189   feholdexcept (&env);
190   fesetround (FE_TONEAREST);
191 #endif
192
193   /* Multiplication m1 + m2 = x * y using Dekker's algorithm.  */
194 #define C ((1LL << (FLT128_MANT_DIG + 1) / 2) + 1)
195   __float128 x1 = x * C;
196   __float128 y1 = y * C;
197   __float128 m1 = x * y;
198   x1 = (x - x1) + x1;
199   y1 = (y - y1) + y1;
200   __float128 x2 = x - x1;
201   __float128 y2 = y - y1;
202   __float128 m2 = (((x1 * y1 - m1) + x1 * y2) + x2 * y1) + x2 * y2;
203
204   /* Addition a1 + a2 = z + m1 using Knuth's algorithm.  */
205   __float128 a1 = z + m1;
206   __float128 t1 = a1 - z;
207   __float128 t2 = a1 - t1;
208   t1 = m1 - t1;
209   t2 = z - t2;
210   __float128 a2 = t1 + t2;
211 #ifdef USE_FENV_H
212   feclearexcept (FE_INEXACT);
213 #endif
214
215   /* If the result is an exact zero, ensure it has the correct
216      sign.  */
217   if (a1 == 0 && m2 == 0)
218     {
219 #ifdef USE_FENV_H
220       feupdateenv (&env);
221 #endif
222       /* Ensure that round-to-nearest value of z + m1 is not
223          reused.  */
224       asm volatile ("" : "=m" (z) : "m" (z));
225       return z + m1;
226     }
227
228
229 #ifdef USE_FENV_H
230   fesetround (FE_TOWARDZERO);
231 #endif
232   /* Perform m2 + a2 addition with round to odd.  */
233   u.value = a2 + m2;
234
235   if (__builtin_expect (adjust == 0, 1))
236     {
237 #ifdef USE_FENV_H
238       if ((u.ieee.mant_low & 1) == 0 && u.ieee.exponent != 0x7fff)
239         u.ieee.mant_low |= fetestexcept (FE_INEXACT) != 0;
240       feupdateenv (&env);
241 #endif
242       /* Result is a1 + u.value.  */
243       return a1 + u.value;
244     }
245   else if (__builtin_expect (adjust > 0, 1))
246     {
247 #ifdef USE_FENV_H
248       if ((u.ieee.mant_low & 1) == 0 && u.ieee.exponent != 0x7fff)
249         u.ieee.mant_low |= fetestexcept (FE_INEXACT) != 0;
250       feupdateenv (&env);
251 #endif
252       /* Result is a1 + u.value, scaled up.  */
253       return (a1 + u.value) * 0x1p113Q;
254     }
255   else
256     {
257 #ifdef USE_FENV_H
258       if ((u.ieee.mant_low & 1) == 0)
259         u.ieee.mant_low |= fetestexcept (FE_INEXACT) != 0;
260 #endif
261       v.value = a1 + u.value;
262       /* Ensure the addition is not scheduled after fetestexcept call.  */
263       asm volatile ("" : : "m" (v.value));
264 #ifdef USE_FENV_H
265       int j = fetestexcept (FE_INEXACT) != 0;
266       feupdateenv (&env);
267 #else
268       int j = 0;
269 #endif
270       /* Ensure the following computations are performed in default rounding
271          mode instead of just reusing the round to zero computation.  */
272       asm volatile ("" : "=m" (u) : "m" (u));
273       /* If a1 + u.value is exact, the only rounding happens during
274          scaling down.  */
275       if (j == 0)
276         return v.value * 0x1p-226Q;
277       /* If result rounded to zero is not subnormal, no double
278          rounding will occur.  */
279       if (v.ieee.exponent > 226)
280         return (a1 + u.value) * 0x1p-226Q;
281       /* If v.value * 0x1p-226Q with round to zero is a subnormal above
282          or equal to FLT128_MIN / 2, then v.value * 0x1p-226Q shifts mantissa
283          down just by 1 bit, which means v.ieee.mant_low |= j would
284          change the round bit, not sticky or guard bit.
285          v.value * 0x1p-226Q never normalizes by shifting up,
286          so round bit plus sticky bit should be already enough
287          for proper rounding.  */
288       if (v.ieee.exponent == 226)
289         {
290           /* If the exponent would be in the normal range when
291              rounding to normal precision with unbounded exponent
292              range, the exact result is known and spurious underflows
293              must be avoided on systems detecting tininess after
294              rounding.  */
295           if (TININESS_AFTER_ROUNDING)
296             {
297               w.value = a1 + u.value;
298               if (w.ieee.exponent == 227)
299                 return w.value * 0x1p-226Q;
300             }
301           /* v.ieee.mant_low & 2 is LSB bit of the result before rounding,
302              v.ieee.mant_low & 1 is the round bit and j is our sticky
303              bit. */
304           w.value = 0.0Q;
305           w.ieee.mant_low = ((v.ieee.mant_low & 3) << 1) | j;
306           w.ieee.negative = v.ieee.negative;
307           v.ieee.mant_low &= ~3U;
308           v.value *= 0x1p-226Q;
309           w.value *= 0x1p-2Q;
310           return v.value + w.value;
311         }
312       v.ieee.mant_low |= j;
313       return v.value * 0x1p-226Q;
314     }
315 }