mirror of git://git.musl-libc.org/musl
math: fix fma bug on x86 (found by Bruno Haible with gnulib)
The long double adjustment was wrong: The usual check is mant_bits & 0x7ff == 0x400 before doing a mant_bits++ or mant_bits-- adjustment since this is the only case when rounding an inexact ld80 into double can go wrong. (only in nearest rounding mode) After such a check the ++ and -- is ok (the mantissa will end in 0x401 or 0x3ff). fma is a bit different (we need to add 3 numbers with correct rounding: hi_xy + lo_xy + z so we should survive two roundings at different places without precision loss) The adjustment in fma only checks for zero low bits mant_bits & 0x3ff == 0 this way the adjusted value is correct when rounded to double or *less* precision. (this is an important piece in the fma puzzle) Unfortunately in this case the -- is not a correct adjustment because mant_bits might underflow so further checks are needed and this was the source of the bug.
This commit is contained in:
parent
ac4fb51dde
commit
e5fb6820a4
|
@ -41,7 +41,7 @@ static void mul(long double *hi, long double *lo, long double x, long double y)
|
|||
|
||||
/*
|
||||
assume (long double)(hi+lo) == hi
|
||||
return an adjusted hi so that rounding it to double is correct
|
||||
return an adjusted hi so that rounding it to double (or less) precision is correct
|
||||
*/
|
||||
static long double adjust(long double hi, long double lo)
|
||||
{
|
||||
|
@ -55,17 +55,25 @@ static long double adjust(long double hi, long double lo)
|
|||
ulo.x = lo;
|
||||
if (uhi.bits.s == ulo.bits.s)
|
||||
uhi.bits.m++;
|
||||
else
|
||||
else {
|
||||
uhi.bits.m--;
|
||||
/* handle underflow and take care of ld80 implicit msb */
|
||||
if (uhi.bits.m == (uint64_t)-1/2) {
|
||||
uhi.bits.m *= 2;
|
||||
uhi.bits.e--;
|
||||
}
|
||||
}
|
||||
return uhi.x;
|
||||
}
|
||||
|
||||
/* adjusted add so the result is correct when rounded to double (or less) precision */
|
||||
static long double dadd(long double x, long double y)
|
||||
{
|
||||
add(&x, &y, x, y);
|
||||
return adjust(x, y);
|
||||
}
|
||||
|
||||
/* adjusted mul so the result is correct when rounded to double (or less) precision */
|
||||
static long double dmul(long double x, long double y)
|
||||
{
|
||||
mul(&x, &y, x, y);
|
||||
|
|
Loading…
Reference in New Issue