1/* mpfr_sub -- subtract two floating-point numbers
2
3Copyright 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4Contributed by the Arenaire and Cacao projects, INRIA.
5
6This file is part of the GNU MPFR Library.
7
8The GNU MPFR Library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as published by
10the Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13The GNU MPFR Library is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16License for more details.
17
18You should have received a copy of the GNU Lesser General Public License
19along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23#include "mpfr-impl.h"
24
25int
26mpfr_sub (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c, mpfr_rnd_t rnd_mode)
27{
28  MPFR_LOG_FUNC (("b[%#R]=%R c[%#R]=%R rnd=%d", b, b, c, c, rnd_mode),
29                 ("a[%#R]=%R", a, a));
30
31  if (MPFR_ARE_SINGULAR (b,c))
32    {
33      if (MPFR_IS_NAN (b) || MPFR_IS_NAN (c))
34        {
35          MPFR_SET_NAN (a);
36          MPFR_RET_NAN;
37        }
38      else if (MPFR_IS_INF (b))
39        {
40          if (!MPFR_IS_INF (c) || MPFR_SIGN (b) != MPFR_SIGN(c))
41            {
42              MPFR_SET_INF (a);
43              MPFR_SET_SAME_SIGN (a, b);
44              MPFR_RET (0); /* exact */
45            }
46          else
47            {
48              MPFR_SET_NAN (a); /* Inf - Inf */
49              MPFR_RET_NAN;
50            }
51        }
52      else if (MPFR_IS_INF (c))
53        {
54          MPFR_SET_INF (a);
55          MPFR_SET_OPPOSITE_SIGN (a, c);
56          MPFR_RET (0); /* exact */
57        }
58      else if (MPFR_IS_ZERO (b))
59        {
60          if (MPFR_IS_ZERO (c))
61            {
62              int sign = rnd_mode != MPFR_RNDD
63                ? ((MPFR_IS_NEG(b) && MPFR_IS_POS(c)) ? -1 : 1)
64                : ((MPFR_IS_POS(b) && MPFR_IS_NEG(c)) ? 1 : -1);
65              MPFR_SET_SIGN (a, sign);
66              MPFR_SET_ZERO (a);
67              MPFR_RET(0); /* 0 - 0 is exact */
68            }
69          else
70            return mpfr_neg (a, c, rnd_mode);
71        }
72      else
73        {
74          MPFR_ASSERTD (MPFR_IS_ZERO (c));
75          return mpfr_set (a, b, rnd_mode);
76        }
77    }
78  MPFR_ASSERTD (MPFR_IS_PURE_FP (b) && MPFR_IS_PURE_FP (c));
79
80  if (MPFR_LIKELY (MPFR_SIGN (b) == MPFR_SIGN (c)))
81    { /* signs are equal, it's a real subtraction */
82      if (MPFR_LIKELY (MPFR_PREC (a) == MPFR_PREC (b)
83                       && MPFR_PREC (b) == MPFR_PREC (c)))
84        return mpfr_sub1sp (a, b, c, rnd_mode);
85      else
86        return mpfr_sub1 (a, b, c, rnd_mode);
87    }
88  else
89    { /* signs differ, it's an addition */
90      if (MPFR_GET_EXP (b) < MPFR_GET_EXP (c))
91         { /* exchange rounding modes toward +/- infinity */
92          int inexact;
93          rnd_mode = MPFR_INVERT_RND (rnd_mode);
94          if (MPFR_LIKELY (MPFR_PREC (a) == MPFR_PREC (b)
95                           && MPFR_PREC (b) == MPFR_PREC (c)))
96            inexact = mpfr_add1sp (a, c, b, rnd_mode);
97          else
98            inexact = mpfr_add1 (a, c, b, rnd_mode);
99          MPFR_CHANGE_SIGN (a);
100          return -inexact;
101        }
102      else
103        {
104          if (MPFR_LIKELY (MPFR_PREC (a) == MPFR_PREC (b)
105                           && MPFR_PREC (b) == MPFR_PREC (c)))
106            return mpfr_add1sp (a, b, c, rnd_mode);
107          else
108            return mpfr_add1 (a, b, c, rnd_mode);
109        }
110    }
111}
112