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