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.
/*
assume (long double)(hi+lo) == hi
/*
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)
{
*/
static long double adjust(long double hi, long double lo)
{
ulo.x = lo;
if (uhi.bits.s == ulo.bits.s)
uhi.bits.m++;
ulo.x = lo;
if (uhi.bits.s == ulo.bits.s)
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--;
+ }
+ }
+/* 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);
}
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);
static long double dmul(long double x, long double y)
{
mul(&x, &y, x, y);