summaryrefslogtreecommitdiff
path: root/fma.c
blob: cbdc2b2f1d1a7eef0f5bc36172b632eb72cc06a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/* mpfr_fma -- Floating multiply-add

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"

/* The computation of fma of x y and u is done by
    fma(s,x,y,z)= z + x*y = s                       */

int
mpfr_fma (mpfr_ptr s, mpfr_srcptr x, mpfr_srcptr y, mpfr_srcptr z,
          mp_rnd_t rnd_mode)
{
  int inexact;
  mpfr_t u;

  /* particular cases */
  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;
        }
      /* now neither x, y or z is NaN */
      else if (MPFR_IS_INF(x) || MPFR_IS_INF(y))
        {
          /* cases Inf*0+z, 0*Inf+z, Inf-Inf */
          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)) /* case Inf-Inf already checked above */
            {
              MPFR_SET_INF(s);
              MPFR_SET_SAME_SIGN(s, z);
              MPFR_RET(0);
            }
          else /* z is finite */
            {
              MPFR_SET_INF(s);
              MPFR_SET_SIGN(s, MPFR_MULT_SIGN(MPFR_SIGN(x) , MPFR_SIGN(y)));
              MPFR_RET(0);
            }
        }
      /* now x and y are finite */
      else if (MPFR_IS_INF(z))
        {
          MPFR_SET_INF(s);
          MPFR_SET_SAME_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_NEG(z))
                                ? -1 : 1) :
                               ((MPFR_IS_POS_SIGN(sign_p) && MPFR_IS_POS(z))
                                ? 1 : -1)));
              MPFR_SET_ZERO(s);
              MPFR_RET(0);
            }
          else
            return mpfr_set (s, z, rnd_mode);
        }
      else /* necessarily z is zero here */
        {
          MPFR_ASSERTD(MPFR_IS_ZERO(z));
          return mpfr_mul (s, x, y, rnd_mode);
        }
    }
  /* Useless since it is done by mpfr_add
   * MPFR_CLEAR_FLAGS(s); */

  /* if we take prec(u) >= prec(x) + prec(y), the product
     u <- x*y is always exact */
  mpfr_init2 (u, MPFR_PREC(x) + MPFR_PREC(y));
  mpfr_mul (u, x, y, GMP_RNDN); /* always exact */
  inexact = mpfr_add (s, z, u, rnd_mode);
  mpfr_clear(u);

  return inexact;
}