Copyright 2001, 2002, 2004, 2006, 2007 Free Software Foundation, Inc.
Contributed by the Arenaire and Cacao projects, INRIA.
This file is part of the MPFR Library.
The MPFR Library is free software; you can redistribute
it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software
Foundation; either version 2.1 of the License, or (at your
option) any later version.
The MPFR Library is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser
General Public License along with the MPFR Library; see
the file COPYING.LIB. If not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301, USA. */
#include "mpfr-impl.h"
fms(x,y,z)= x*y - z
Note: this is neither in IEEE754R, nor in LIA-2, but both the
PowerPC and the Itanium define fms as x*y - z.
*/
int
mpfr_fms (mpfr_ptr s, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z,
mp_rnd_t rnd_mode)
{
int inexact;
mpfr_t u;
MPFR_SAVE_EXPO_DECL (expo);
if (MPFR_UNLIKELY( MPFR_IS_SINGULAR(x) ||
MPFR_IS_SINGULAR(y) ||
MPFR_IS_SINGULAR(z) ))
{
if (MPFR_IS_NAN(x) || MPFR_IS_NAN(y) || MPFR_IS_NAN(z))
{
MPFR_SET_NAN(s);
MPFR_RET_NAN;
}
else if (MPFR_IS_INF(x) || MPFR_IS_INF(y))
{
if ((MPFR_IS_ZERO(y)) ||
(MPFR_IS_ZERO(x)) ||
(MPFR_IS_INF(z) &&
((MPFR_MULT_SIGN(MPFR_SIGN(x), MPFR_SIGN(y))) == MPFR_SIGN(z))))
{
MPFR_SET_NAN(s);
MPFR_RET_NAN;
}
else if (MPFR_IS_INF(z))
{
MPFR_SET_INF(s);
MPFR_SET_OPPOSITE_SIGN(s, z);
MPFR_RET(0);
}
else
{
MPFR_SET_INF(s);
MPFR_SET_SIGN(s, MPFR_MULT_SIGN(MPFR_SIGN(x) , MPFR_SIGN(y)));
MPFR_RET(0);
}
}
else if (MPFR_IS_INF(z))
{
MPFR_SET_INF(s);
MPFR_SET_OPPOSITE_SIGN(s, z);
MPFR_RET(0);
}
else if (MPFR_IS_ZERO(x) || MPFR_IS_ZERO(y))
{
if (MPFR_IS_ZERO(z))
{
int sign_p;
sign_p = MPFR_MULT_SIGN( MPFR_SIGN(x) , MPFR_SIGN(y) );
MPFR_SET_SIGN(s,(rnd_mode != GMP_RNDD ?
((MPFR_IS_NEG_SIGN(sign_p) && MPFR_IS_POS(z))
? -1 : 1) :
((MPFR_IS_POS_SIGN(sign_p) && MPFR_IS_NEG(z))
? 1 : -1)));
MPFR_SET_ZERO(s);
MPFR_RET(0);
}
else
return mpfr_neg (s, z, rnd_mode);
}
else
{
MPFR_ASSERTD(MPFR_IS_ZERO(z));
return mpfr_mul (s, x, y, rnd_mode);
}
}
is exact, except in case of overflow or underflow. */
MPFR_SAVE_EXPO_MARK (expo);
mpfr_init2 (u, MPFR_PREC(x) + MPFR_PREC(y));
if (MPFR_UNLIKELY (mpfr_mul (u, x, y, GMP_RNDN)))
{
does not need to be very efficient (even if some tests below
could have been done earlier).
It is an overflow iff u is an infinity (since GMP_RNDN was used).
Alternatively, we could test the overflow flag, but in this case,
mpfr_clear_flags would have been necessary. */
if (MPFR_IS_INF (u))
{
same sign. No possible cancellation -> real overflow.
Also, we know that |z| < 2^emax. If E(x) + E(y) >= emax+3,
then |x*y| >= 2^(emax+1), and |x*y - z| >= 2^emax. This case
is also an overflow. */
if (MPFR_SIGN (u) != MPFR_SIGN (z) ||
MPFR_GET_EXP (x) + MPFR_GET_EXP (y) >= __gmpfr_emax + 3)
{
mpfr_clear (u);
MPFR_SAVE_EXPO_FREE (expo);
return mpfr_overflow (s, rnd_mode, - MPFR_SIGN (z));
}
(x/4)*y does not overflow (let's recall that the result
is exact with an unbounded exponent range). It does not
underflow either, because x*y overflows and the exponent
range is large enough. */
inexact = mpfr_div_2ui (u, x, 2, GMP_RNDN);
MPFR_ASSERTN (inexact == 0);
inexact = mpfr_mul (u, u, y, GMP_RNDN);
MPFR_ASSERTN (inexact == 0);
{
mpfr_t zo4;
mpfr_srcptr zz;
if (MPFR_GET_EXP (u) > MPFR_GET_EXP (z) &&
MPFR_GET_EXP (u) - MPFR_GET_EXP (z) > MPFR_PREC (u))
{
zz = z;
}
else
{
mpfr_init2 (zo4, MPFR_PREC (z));
if (mpfr_div_2ui (zo4, z, 2, GMP_RNDZ))
{
MPFR_ASSERTN (0);
}
zz = zo4;
}
mpfr_clear_flags ();
following subtraction would give the same result). */
inexact = mpfr_sub (s, u, zz, rnd_mode);
is not possible. But an underflow is theoretically
possible! */
if (mpfr_underflow_p ())
{
MPFR_ASSERTN (zz != z);
MPFR_ASSERTN (0);
mpfr_clears (zo4, u, (void *) 0);
}
else
{
int inex2;
if (zz != z)
mpfr_clear (zo4);
mpfr_clear (u);
MPFR_ASSERTN (! mpfr_overflow_p ());
inex2 = mpfr_mul_2ui (s, s, 2, rnd_mode);
if (inex2)
{
inexact = inex2;
MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, __gmpfr_flags);
}
goto end;
}
}
}
else
{
unsigned long scale = 0;
mpfr_t scaled_z;
mpfr_srcptr new_z;
mp_exp_t diffexp;
mp_prec_t pzs;
int xy_underflows;
(the + 1 on MPFR_PREC (s) is necessary because the exponent
of the result can be EXP(z) - 1). */
diffexp = MPFR_GET_EXP (z) - __gmpfr_emin;
pzs = MAX (MPFR_PREC (z), MPFR_PREC (s) + 1);
if (diffexp <= pzs)
{
mp_exp_unsigned_t uscale;
mpfr_t scaled_v;
uscale = (mp_exp_unsigned_t) pzs - diffexp + 1;
MPFR_ASSERTN (uscale > 0);
MPFR_ASSERTN (uscale <= ULONG_MAX);
scale = uscale;
mpfr_init2 (scaled_z, MPFR_PREC (z));
inexact = mpfr_mul_2ui (scaled_z, z, scale, GMP_RNDN);
MPFR_ASSERTN (inexact == 0);
new_z = scaled_z;
mpfr_clear_flags ();
if (MPFR_GET_EXP (x) < MPFR_GET_EXP (y))
{
mpfr_init2 (scaled_v, MPFR_PREC (x));
mpfr_mul_2ui (scaled_v, x, scale, GMP_RNDN);
mpfr_mul (u, scaled_v, y, GMP_RNDN);
}
else
{
mpfr_init2 (scaled_v, MPFR_PREC (y));
mpfr_mul_2ui (scaled_v, y, scale, GMP_RNDN);
mpfr_mul (u, x, scaled_v, GMP_RNDN);
}
mpfr_clear (scaled_v);
MPFR_ASSERTN (! mpfr_overflow_p ());
xy_underflows = mpfr_underflow_p ();
}
else
{
new_z = z;
xy_underflows = 1;
}
if (xy_underflows)
{
mpfr_set_prec (u, MPFR_PREC_MIN);
mpfr_setmin (u, __gmpfr_emin);
MPFR_SET_SIGN (u, MPFR_MULT_SIGN (MPFR_SIGN (x),
MPFR_SIGN (y)));
}
mpfr_clear_flags ();
inexact = mpfr_sub (s, u, new_z, rnd_mode);
mpfr_clear (u);
if (scale != 0)
{
int inex2;
mpfr_clear (scaled_z);
the result may be wrong, hence the assert. An underflow
is not possible, but let's check that anyway. */
MPFR_ASSERTN (! mpfr_overflow_p ());
MPFR_ASSERTN (! mpfr_underflow_p ());
inex2 = mpfr_div_2ui (s, s, scale, GMP_RNDN);
if (inex2)
inexact = inex2;
}
MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, __gmpfr_flags);
goto end;
}
}
inexact = mpfr_sub (s, u, z, rnd_mode);
mpfr_clear (u);
MPFR_SAVE_EXPO_UPDATE_FLAGS (expo, __gmpfr_flags);
end:
MPFR_SAVE_EXPO_FREE (expo);
return mpfr_check_range (s, inexact, rnd_mode);
}