Merge remote-tracking branch 'nsz/math'
[musl] / src / math / fma.c
1 #include <fenv.h>
2 #include "libm.h"
3
4 #if LDBL_MANT_DIG==64 && LDBL_MAX_EXP==16384
5 union ld80 {
6         long double x;
7         struct {
8                 uint64_t m;
9                 uint16_t e : 15;
10                 uint16_t s : 1;
11                 uint16_t pad;
12         } bits;
13 };
14
15 /* exact add, assumes exponent_x >= exponent_y */
16 static void add(long double *hi, long double *lo, long double x, long double y)
17 {
18         long double r;
19
20         r = x + y;
21         *hi = r;
22         r -= x;
23         *lo = y - r;
24 }
25
26 /* exact mul, assumes no over/underflow */
27 static void mul(long double *hi, long double *lo, long double x, long double y)
28 {
29         static const long double c = 1.0 + 0x1p32L;
30         long double cx, xh, xl, cy, yh, yl;
31
32         cx = c*x;
33         xh = (x - cx) + cx;
34         xl = x - xh;
35         cy = c*y;
36         yh = (y - cy) + cy;
37         yl = y - yh;
38         *hi = x*y;
39         *lo = (xh*yh - *hi) + xh*yl + xl*yh + xl*yl;
40 }
41
42 /*
43 assume (long double)(hi+lo) == hi
44 return an adjusted hi so that rounding it to double (or less) precision is correct
45 */
46 static long double adjust(long double hi, long double lo)
47 {
48         union ld80 uhi, ulo;
49
50         if (lo == 0)
51                 return hi;
52         uhi.x = hi;
53         if (uhi.bits.m & 0x3ff)
54                 return hi;
55         ulo.x = lo;
56         if (uhi.bits.s == ulo.bits.s)
57                 uhi.bits.m++;
58         else {
59                 uhi.bits.m--;
60                 /* handle underflow and take care of ld80 implicit msb */
61                 if (uhi.bits.m == (uint64_t)-1/2) {
62                         uhi.bits.m *= 2;
63                         uhi.bits.e--;
64                 }
65         }
66         return uhi.x;
67 }
68
69 /* adjusted add so the result is correct when rounded to double (or less) precision */
70 static long double dadd(long double x, long double y)
71 {
72         add(&x, &y, x, y);
73         return adjust(x, y);
74 }
75
76 /* adjusted mul so the result is correct when rounded to double (or less) precision */
77 static long double dmul(long double x, long double y)
78 {
79         mul(&x, &y, x, y);
80         return adjust(x, y);
81 }
82
83 static int getexp(long double x)
84 {
85         union ld80 u;
86         u.x = x;
87         return u.bits.e;
88 }
89
90 double fma(double x, double y, double z)
91 {
92         #pragma STDC FENV_ACCESS ON
93         long double hi, lo1, lo2, xy;
94         int round, ez, exy;
95
96         /* handle +-inf,nan */
97         if (!isfinite(x) || !isfinite(y))
98                 return x*y + z;
99         if (!isfinite(z))
100                 return z;
101         /* handle +-0 */
102         if (x == 0.0 || y == 0.0)
103                 return x*y + z;
104         round = fegetround();
105         if (z == 0.0) {
106                 if (round == FE_TONEAREST)
107                         return dmul(x, y);
108                 return x*y;
109         }
110
111         /* exact mul and add require nearest rounding */
112         /* spurious inexact exceptions may be raised */
113         fesetround(FE_TONEAREST);
114         mul(&xy, &lo1, x, y);
115         exy = getexp(xy);
116         ez = getexp(z);
117         if (ez > exy) {
118                 add(&hi, &lo2, z, xy);
119         } else if (ez > exy - 12) {
120                 add(&hi, &lo2, xy, z);
121                 if (hi == 0) {
122                         fesetround(round);
123                         /* make sure that the sign of 0 is correct */
124                         return (xy + z) + lo1;
125                 }
126         } else {
127                 /*
128                 ez <= exy - 12
129                 the 12 extra bits (1guard, 11round+sticky) are needed so with
130                         lo = dadd(lo1, lo2)
131                 elo <= ehi - 11, and we use the last 10 bits in adjust so
132                         dadd(hi, lo)
133                 gives correct result when rounded to double
134                 */
135                 hi = xy;
136                 lo2 = z;
137         }
138         fesetround(round);
139         if (round == FE_TONEAREST)
140                 return dadd(hi, dadd(lo1, lo2));
141         return hi + (lo1 + lo2);
142 }
143 #else
144 /* origin: FreeBSD /usr/src/lib/msun/src/s_fma.c */
145 /*-
146  * Copyright (c) 2005-2011 David Schultz <das@FreeBSD.ORG>
147  * All rights reserved.
148  *
149  * Redistribution and use in source and binary forms, with or without
150  * modification, are permitted provided that the following conditions
151  * are met:
152  * 1. Redistributions of source code must retain the above copyright
153  *    notice, this list of conditions and the following disclaimer.
154  * 2. Redistributions in binary form must reproduce the above copyright
155  *    notice, this list of conditions and the following disclaimer in the
156  *    documentation and/or other materials provided with the distribution.
157  *
158  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
161  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
162  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
163  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
164  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
165  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
166  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
167  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
168  * SUCH DAMAGE.
169  */
170
171 /*
172  * A struct dd represents a floating-point number with twice the precision
173  * of a double.  We maintain the invariant that "hi" stores the 53 high-order
174  * bits of the result.
175  */
176 struct dd {
177         double hi;
178         double lo;
179 };
180
181 /*
182  * Compute a+b exactly, returning the exact result in a struct dd.  We assume
183  * that both a and b are finite, but make no assumptions about their relative
184  * magnitudes.
185  */
186 static inline struct dd dd_add(double a, double b)
187 {
188         struct dd ret;
189         double s;
190
191         ret.hi = a + b;
192         s = ret.hi - a;
193         ret.lo = (a - (ret.hi - s)) + (b - s);
194         return (ret);
195 }
196
197 /*
198  * Compute a+b, with a small tweak:  The least significant bit of the
199  * result is adjusted into a sticky bit summarizing all the bits that
200  * were lost to rounding.  This adjustment negates the effects of double
201  * rounding when the result is added to another number with a higher
202  * exponent.  For an explanation of round and sticky bits, see any reference
203  * on FPU design, e.g.,
204  *
205  *     J. Coonen.  An Implementation Guide to a Proposed Standard for
206  *     Floating-Point Arithmetic.  Computer, vol. 13, no. 1, Jan 1980.
207  */
208 static inline double add_adjusted(double a, double b)
209 {
210         struct dd sum;
211         uint64_t hibits, lobits;
212
213         sum = dd_add(a, b);
214         if (sum.lo != 0) {
215                 EXTRACT_WORD64(hibits, sum.hi);
216                 if ((hibits & 1) == 0) {
217                         /* hibits += (int)copysign(1.0, sum.hi * sum.lo) */
218                         EXTRACT_WORD64(lobits, sum.lo);
219                         hibits += 1 - ((hibits ^ lobits) >> 62);
220                         INSERT_WORD64(sum.hi, hibits);
221                 }
222         }
223         return (sum.hi);
224 }
225
226 /*
227  * Compute ldexp(a+b, scale) with a single rounding error. It is assumed
228  * that the result will be subnormal, and care is taken to ensure that
229  * double rounding does not occur.
230  */
231 static inline double add_and_denormalize(double a, double b, int scale)
232 {
233         struct dd sum;
234         uint64_t hibits, lobits;
235         int bits_lost;
236
237         sum = dd_add(a, b);
238
239         /*
240          * If we are losing at least two bits of accuracy to denormalization,
241          * then the first lost bit becomes a round bit, and we adjust the
242          * lowest bit of sum.hi to make it a sticky bit summarizing all the
243          * bits in sum.lo. With the sticky bit adjusted, the hardware will
244          * break any ties in the correct direction.
245          *
246          * If we are losing only one bit to denormalization, however, we must
247          * break the ties manually.
248          */
249         if (sum.lo != 0) {
250                 EXTRACT_WORD64(hibits, sum.hi);
251                 bits_lost = -((int)(hibits >> 52) & 0x7ff) - scale + 1;
252                 if (bits_lost != 1 ^ (int)(hibits & 1)) {
253                         /* hibits += (int)copysign(1.0, sum.hi * sum.lo) */
254                         EXTRACT_WORD64(lobits, sum.lo);
255                         hibits += 1 - (((hibits ^ lobits) >> 62) & 2);
256                         INSERT_WORD64(sum.hi, hibits);
257                 }
258         }
259         return scalbn(sum.hi, scale);
260 }
261
262 /*
263  * Compute a*b exactly, returning the exact result in a struct dd.  We assume
264  * that both a and b are normalized, so no underflow or overflow will occur.
265  * The current rounding mode must be round-to-nearest.
266  */
267 static inline struct dd dd_mul(double a, double b)
268 {
269         static const double split = 0x1p27 + 1.0;
270         struct dd ret;
271         double ha, hb, la, lb, p, q;
272
273         p = a * split;
274         ha = a - p;
275         ha += p;
276         la = a - ha;
277
278         p = b * split;
279         hb = b - p;
280         hb += p;
281         lb = b - hb;
282
283         p = ha * hb;
284         q = ha * lb + la * hb;
285
286         ret.hi = p + q;
287         ret.lo = p - ret.hi + q + la * lb;
288         return (ret);
289 }
290
291 /*
292  * Fused multiply-add: Compute x * y + z with a single rounding error.
293  *
294  * We use scaling to avoid overflow/underflow, along with the
295  * canonical precision-doubling technique adapted from:
296  *
297  *      Dekker, T.  A Floating-Point Technique for Extending the
298  *      Available Precision.  Numer. Math. 18, 224-242 (1971).
299  *
300  * This algorithm is sensitive to the rounding precision.  FPUs such
301  * as the i387 must be set in double-precision mode if variables are
302  * to be stored in FP registers in order to avoid incorrect results.
303  * This is the default on FreeBSD, but not on many other systems.
304  *
305  * Hardware instructions should be used on architectures that support it,
306  * since this implementation will likely be several times slower.
307  */
308 double fma(double x, double y, double z)
309 {
310         #pragma STDC FENV_ACCESS ON
311         double xs, ys, zs, adj;
312         struct dd xy, r;
313         int oround;
314         int ex, ey, ez;
315         int spread;
316
317         /*
318          * Handle special cases. The order of operations and the particular
319          * return values here are crucial in handling special cases involving
320          * infinities, NaNs, overflows, and signed zeroes correctly.
321          */
322         if (!isfinite(x) || !isfinite(y))
323                 return (x * y + z);
324         if (!isfinite(z))
325                 return (z);
326         if (x == 0.0 || y == 0.0)
327                 return (x * y + z);
328         if (z == 0.0)
329                 return (x * y);
330
331         xs = frexp(x, &ex);
332         ys = frexp(y, &ey);
333         zs = frexp(z, &ez);
334         oround = fegetround();
335         spread = ex + ey - ez;
336
337         /*
338          * If x * y and z are many orders of magnitude apart, the scaling
339          * will overflow, so we handle these cases specially.  Rounding
340          * modes other than FE_TONEAREST are painful.
341          */
342         if (spread < -DBL_MANT_DIG) {
343 #ifdef FE_INEXACT
344                 feraiseexcept(FE_INEXACT);
345 #endif
346 #ifdef FE_UNDERFLOW
347                 if (!isnormal(z))
348                         feraiseexcept(FE_UNDERFLOW);
349 #endif
350                 switch (oround) {
351                 default: /* FE_TONEAREST */
352                         return (z);
353 #ifdef FE_TOWARDZERO
354                 case FE_TOWARDZERO:
355                         if (x > 0.0 ^ y < 0.0 ^ z < 0.0)
356                                 return (z);
357                         else
358                                 return (nextafter(z, 0));
359 #endif
360 #ifdef FE_DOWNWARD
361                 case FE_DOWNWARD:
362                         if (x > 0.0 ^ y < 0.0)
363                                 return (z);
364                         else
365                                 return (nextafter(z, -INFINITY));
366 #endif
367 #ifdef FE_UPWARD
368                 case FE_UPWARD:
369                         if (x > 0.0 ^ y < 0.0)
370                                 return (nextafter(z, INFINITY));
371                         else
372                                 return (z);
373 #endif
374                 }
375         }
376         if (spread <= DBL_MANT_DIG * 2)
377                 zs = scalbn(zs, -spread);
378         else
379                 zs = copysign(DBL_MIN, zs);
380
381         fesetround(FE_TONEAREST);
382
383         /*
384          * Basic approach for round-to-nearest:
385          *
386          *     (xy.hi, xy.lo) = x * y           (exact)
387          *     (r.hi, r.lo)   = xy.hi + z       (exact)
388          *     adj = xy.lo + r.lo               (inexact; low bit is sticky)
389          *     result = r.hi + adj              (correctly rounded)
390          */
391         xy = dd_mul(xs, ys);
392         r = dd_add(xy.hi, zs);
393
394         spread = ex + ey;
395
396         if (r.hi == 0.0) {
397                 /*
398                  * When the addends cancel to 0, ensure that the result has
399                  * the correct sign.
400                  */
401                 fesetround(oround);
402                 volatile double vzs = zs; /* XXX gcc CSE bug workaround */
403                 return xy.hi + vzs + scalbn(xy.lo, spread);
404         }
405
406         if (oround != FE_TONEAREST) {
407                 /*
408                  * There is no need to worry about double rounding in directed
409                  * rounding modes.
410                  */
411                 fesetround(oround);
412                 adj = r.lo + xy.lo;
413                 return scalbn(r.hi + adj, spread);
414         }
415
416         adj = add_adjusted(r.lo, xy.lo);
417         if (spread + ilogb(r.hi) > -1023)
418                 return scalbn(r.hi + adj, spread);
419         else
420                 return add_and_denormalize(r.hi, adj, spread);
421 }
422 #endif