123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- #include "cdefs-compat.h"
- #include <float.h>
- #include <openlibm_fenv.h>
- #include <openlibm_math.h>
- #include "math_private.h"
- struct dd {
- double hi;
- double lo;
- };
- static inline struct dd
- dd_add(double a, double b)
- {
- struct dd ret;
- double s;
- ret.hi = a + b;
- s = ret.hi - a;
- ret.lo = (a - (ret.hi - s)) + (b - s);
- return (ret);
- }
- static inline double
- add_adjusted(double a, double b)
- {
- struct dd sum;
- u_int64_t hibits, lobits;
- sum = dd_add(a, b);
- if (sum.lo != 0) {
- EXTRACT_WORD64(hibits, sum.hi);
- if ((hibits & 1) == 0) {
-
- EXTRACT_WORD64(lobits, sum.lo);
- hibits += 1 - ((hibits ^ lobits) >> 62);
- INSERT_WORD64(sum.hi, hibits);
- }
- }
- return (sum.hi);
- }
- static inline double
- add_and_denormalize(double a, double b, int scale)
- {
- struct dd sum;
- u_int64_t hibits, lobits;
- int bits_lost;
- sum = dd_add(a, b);
-
- if (sum.lo != 0) {
- EXTRACT_WORD64(hibits, sum.hi);
- bits_lost = -((int)(hibits >> 52) & 0x7ff) - scale + 1;
- if ((bits_lost != 1) ^ (int)(hibits & 1)) {
-
- EXTRACT_WORD64(lobits, sum.lo);
- hibits += 1 - (((hibits ^ lobits) >> 62) & 2);
- INSERT_WORD64(sum.hi, hibits);
- }
- }
- return (ldexp(sum.hi, scale));
- }
- static inline struct dd
- dd_mul(double a, double b)
- {
- static const double split = 0x1p27 + 1.0;
- struct dd ret;
- double ha, hb, la, lb, p, q;
- p = a * split;
- ha = a - p;
- ha += p;
- la = a - ha;
- p = b * split;
- hb = b - p;
- hb += p;
- lb = b - hb;
- p = ha * hb;
- q = ha * lb + la * hb;
- ret.hi = p + q;
- ret.lo = p - ret.hi + q + la * lb;
- return (ret);
- }
- OLM_DLLEXPORT double
- fma(double x, double y, double z)
- {
- double xs, ys, zs, adj;
- struct dd xy, r;
- int oround;
- int ex, ey, ez;
- int spread;
-
- if (x == 0.0 || y == 0.0)
- return (x * y + z);
- if (z == 0.0)
- return (x * y);
- if (!isfinite(x) || !isfinite(y))
- return (x * y + z);
- if (!isfinite(z))
- return (z);
- xs = frexp(x, &ex);
- ys = frexp(y, &ey);
- zs = frexp(z, &ez);
- oround = fegetround();
- spread = ex + ey - ez;
-
- if (spread < -DBL_MANT_DIG) {
- feraiseexcept(FE_INEXACT);
- if (!isnormal(z))
- feraiseexcept(FE_UNDERFLOW);
- switch (oround) {
- case FE_TONEAREST:
- return (z);
- case FE_TOWARDZERO:
- if ((x > 0.0) ^ (y < 0.0) ^ (z < 0.0))
- return (z);
- else
- return (nextafter(z, 0));
- case FE_DOWNWARD:
- if ((x > 0.0) ^ (y < 0.0))
- return (z);
- else
- return (nextafter(z, -INFINITY));
- default:
- if ((x > 0.0) ^ (y < 0.0))
- return (nextafter(z, INFINITY));
- else
- return (z);
- }
- }
- if (spread <= DBL_MANT_DIG * 2)
- zs = ldexp(zs, -spread);
- else
- zs = copysign(DBL_MIN, zs);
- fesetround(FE_TONEAREST);
-
- xy = dd_mul(xs, ys);
- r = dd_add(xy.hi, zs);
- spread = ex + ey;
- if (r.hi == 0.0) {
-
- fesetround(oround);
- volatile double vzs = zs;
- return (xy.hi + vzs + ldexp(xy.lo, spread));
- }
- if (oround != FE_TONEAREST) {
-
- fesetround(oround);
- adj = r.lo + xy.lo;
- return (ldexp(r.hi + adj, spread));
- }
- adj = add_adjusted(r.lo, xy.lo);
- if (spread + ilogb(r.hi) > -1023)
- return (ldexp(r.hi + adj, spread));
- else
- return (add_and_denormalize(r.hi, adj, spread));
- }
- #if (LDBL_MANT_DIG == 53)
- __weak_reference(fma, fmal);
- #endif
|