X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Fmath%2Ffmal.c;h=4506aac6f6abbe9545f0674dd645c121f5198617;hb=0ab97350f01b42de0f9fd811ee08653169661859;hp=ccbe434d0eae6b883fc07e48bc3b5b79f9c971ff;hpb=8bb181622222f2ee3462c8b021bcae4fcdbbd37a;p=musl diff --git a/src/math/fmal.c b/src/math/fmal.c index ccbe434d..4506aac6 100644 --- a/src/math/fmal.c +++ b/src/math/fmal.c @@ -34,6 +34,13 @@ long double fmal(long double x, long double y, long double z) } #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384 #include +#if LDBL_MANT_DIG == 64 +#define LASTBIT(u) (u.i.m & 1) +#define SPLIT (0x1p32L + 1) +#elif LDBL_MANT_DIG == 113 +#define LASTBIT(u) (u.i.lo & 1) +#define SPLIT (0x1p57L + 1) +#endif /* * A struct dd represents a floating-point number with twice the precision @@ -75,12 +82,12 @@ static inline struct dd dd_add(long double a, long double b) static inline long double add_adjusted(long double a, long double b) { struct dd sum; - union IEEEl2bits u; + union ldshape u; sum = dd_add(a, b); if (sum.lo != 0) { - u.e = sum.hi; - if ((u.bits.manl & 1) == 0) + u.f = sum.hi; + if (!LASTBIT(u)) sum.hi = nextafterl(sum.hi, INFINITY * sum.lo); } return (sum.hi); @@ -95,7 +102,7 @@ static inline long double add_and_denormalize(long double a, long double b, int { struct dd sum; int bits_lost; - union IEEEl2bits u; + union ldshape u; sum = dd_add(a, b); @@ -110,9 +117,9 @@ static inline long double add_and_denormalize(long double a, long double b, int * break the ties manually. */ if (sum.lo != 0) { - u.e = sum.hi; - bits_lost = -u.bits.exp - scale + 1; - if (bits_lost != 1 ^ (int)(u.bits.manl & 1)) + u.f = sum.hi; + bits_lost = -u.i.se - scale + 1; + if ((bits_lost != 1) ^ LASTBIT(u)) sum.hi = nextafterl(sum.hi, INFINITY * sum.lo); } return scalbnl(sum.hi, scale); @@ -125,20 +132,15 @@ static inline long double add_and_denormalize(long double a, long double b, int */ static inline struct dd dd_mul(long double a, long double b) { -#if LDBL_MANT_DIG == 64 - static const long double split = 0x1p32L + 1.0; -#elif LDBL_MANT_DIG == 113 - static const long double split = 0x1p57L + 1.0; -#endif struct dd ret; long double ha, hb, la, lb, p, q; - p = a * split; + p = a * SPLIT; ha = a - p; ha += p; la = a - ha; - p = b * split; + p = b * SPLIT; hb = b - p; hb += p; lb = b - hb; @@ -262,10 +264,24 @@ long double fmal(long double x, long double y, long double z) /* * There is no need to worry about double rounding in directed * rounding modes. + * But underflow may not be raised correctly, example in downward rounding: + * fmal(0x1.0000000001p-16000L, 0x1.0000000001p-400L, -0x1p-16440L) */ + long double ret; +#if defined(FE_INEXACT) && defined(FE_UNDERFLOW) + int e = fetestexcept(FE_INEXACT); + feclearexcept(FE_INEXACT); +#endif fesetround(oround); adj = r.lo + xy.lo; - return scalbnl(r.hi + adj, spread); + ret = scalbnl(r.hi + adj, spread); +#if defined(FE_INEXACT) && defined(FE_UNDERFLOW) + if (ilogbl(ret) < -16382 && fetestexcept(FE_INEXACT)) + feraiseexcept(FE_UNDERFLOW); + else if (e) + feraiseexcept(FE_INEXACT); +#endif + return ret; } adj = add_adjusted(r.lo, xy.lo);