source: trunk/third/gmp/mpf/sub.c @ 22254

Revision 22254, 9.3 KB checked in by ghudson, 19 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r22253, which included commits to RCS files with non-trunk default branches.
Line 
1/* mpf_sub -- Subtract two floats.
2
3Copyright 1993, 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004 Free Software
4Foundation, Inc.
5
6This file is part of the GNU MP Library.
7
8The GNU MP 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 2.1 of the License, or (at your
11option) any later version.
12
13The GNU MP 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 MP Library; see the file COPYING.LIB.  If not, write to
20the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21MA 02111-1307, USA. */
22
23#include "gmp.h"
24#include "gmp-impl.h"
25
26void
27mpf_sub (mpf_ptr r, mpf_srcptr u, mpf_srcptr v)
28{
29  mp_srcptr up, vp;
30  mp_ptr rp, tp;
31  mp_size_t usize, vsize, rsize;
32  mp_size_t prec;
33  mp_exp_t exp;
34  mp_size_t ediff;
35  int negate;
36  TMP_DECL (marker);
37
38  usize = u->_mp_size;
39  vsize = v->_mp_size;
40
41  /* Handle special cases that don't work in generic code below.  */
42  if (usize == 0)
43    {
44      mpf_neg (r, v);
45      return;
46    }
47  if (vsize == 0)
48    {
49      if (r != u)
50        mpf_set (r, u);
51      return;
52    }
53
54  /* If signs of U and V are different, perform addition.  */
55  if ((usize ^ vsize) < 0)
56    {
57      __mpf_struct v_negated;
58      v_negated._mp_size = -vsize;
59      v_negated._mp_exp = v->_mp_exp;
60      v_negated._mp_d = v->_mp_d;
61      mpf_add (r, u, &v_negated);
62      return;
63    }
64
65  TMP_MARK (marker);
66
67  /* Signs are now known to be the same.  */
68  negate = usize < 0;
69
70  /* Make U be the operand with the largest exponent.  */
71  if (u->_mp_exp < v->_mp_exp)
72    {
73      mpf_srcptr t;
74      t = u; u = v; v = t;
75      negate ^= 1;
76      usize = u->_mp_size;
77      vsize = v->_mp_size;
78    }
79
80  usize = ABS (usize);
81  vsize = ABS (vsize);
82  up = u->_mp_d;
83  vp = v->_mp_d;
84  rp = r->_mp_d;
85  prec = r->_mp_prec + 1;
86  exp = u->_mp_exp;
87  ediff = u->_mp_exp - v->_mp_exp;
88
89  /* If ediff is 0 or 1, we might have a situation where the operands are
90     extremely close.  We need to scan the operands from the most significant
91     end ignore the initial parts that are equal.  */
92  if (ediff <= 1)
93    {
94      if (ediff == 0)
95        {
96          /* Skip leading limbs in U and V that are equal.  */
97          if (up[usize - 1] == vp[vsize - 1])
98            {
99              /* This loop normally exits immediately.  Optimize for that.  */
100              do
101                {
102                  usize--;
103                  vsize--;
104                  exp--;
105
106                  if (usize == 0)
107                    {
108                      /* u cancels high limbs of v, result is rest of v */
109                      negate ^= 1;
110                    cancellation:
111                      /* strip high zeros before truncating to prec */
112                      while (vsize != 0 && vp[vsize - 1] == 0)
113                        {
114                          vsize--;
115                          exp--;
116                        }
117                      if (vsize > prec)
118                        {
119                          vp += vsize - prec;
120                          vsize = prec;
121                        }
122                      MPN_COPY_INCR (rp, vp, vsize);
123                      rsize = vsize;
124                      goto done;
125                    }
126                  if (vsize == 0)
127                    {
128                      vp = up;
129                      vsize = usize;
130                      goto cancellation;
131                    }
132                }
133              while (up[usize - 1] == vp[vsize - 1]);
134            }
135
136          if (up[usize - 1] < vp[vsize - 1])
137            {
138              /* For simplicity, swap U and V.  Note that since the loop above
139                 wouldn't have exited unless up[usize - 1] and vp[vsize - 1]
140                 were non-equal, this if-statement catches all cases where U
141                 is smaller than V.  */
142              MPN_SRCPTR_SWAP (up,usize, vp,vsize);
143              negate ^= 1;
144              /* negating ediff not necessary since it is 0.  */
145            }
146
147          /* Check for
148             x+1 00000000 ...
149              x  ffffffff ... */
150          if (up[usize - 1] != vp[vsize - 1] + 1)
151            goto general_case;
152          usize--;
153          vsize--;
154          exp--;
155        }
156      else /* ediff == 1 */
157        {
158          /* Check for
159             1 00000000 ...
160             0 ffffffff ... */
161
162          if (up[usize - 1] != 1 || vp[vsize - 1] != GMP_NUMB_MAX
163              || (usize >= 2 && up[usize - 2] != 0))
164            goto general_case;
165
166          usize--;
167          exp--;
168        }
169
170      /* Skip sequences of 00000000/ffffffff */
171      while (vsize != 0 && usize != 0 && up[usize - 1] == 0
172             && vp[vsize - 1] == GMP_NUMB_MAX)
173        {
174          usize--;
175          vsize--;
176          exp--;
177        }
178
179      if (usize == 0)
180        {
181          while (vsize != 0 && vp[vsize - 1] == GMP_NUMB_MAX)
182            {
183              vsize--;
184              exp--;
185            }
186        }
187
188      if (usize > prec - 1)
189        {
190          up += usize - (prec - 1);
191          usize = prec - 1;
192        }
193      if (vsize > prec - 1)
194        {
195          vp += vsize - (prec - 1);
196          vsize = prec - 1;
197        }
198
199      tp = (mp_ptr) TMP_ALLOC (prec * BYTES_PER_MP_LIMB);
200      {
201        mp_limb_t cy_limb;
202        if (vsize == 0)
203          {
204            mp_size_t size, i;
205            size = usize;
206            for (i = 0; i < size; i++)
207              tp[i] = up[i];
208            tp[size] = 1;
209            rsize = size + 1;
210            exp++;
211            goto normalize;
212          }
213        if (usize == 0)
214          {
215            mp_size_t size, i;
216            size = vsize;
217            for (i = 0; i < size; i++)
218              tp[i] = ~vp[i] & GMP_NUMB_MASK;
219            cy_limb = 1 - mpn_add_1 (tp, tp, vsize, (mp_limb_t) 1);
220            rsize = vsize;
221            if (cy_limb == 0)
222              {
223                tp[rsize] = 1;
224                rsize++;
225                exp++;
226              }
227            goto normalize;
228          }
229        if (usize >= vsize)
230          {
231            /* uuuu     */
232            /* vv       */
233            mp_size_t size;
234            size = usize - vsize;
235            MPN_COPY (tp, up, size);
236            cy_limb = mpn_sub_n (tp + size, up + size, vp, vsize);
237            rsize = usize;
238          }
239        else /* (usize < vsize) */
240          {
241            /* uuuu     */
242            /* vvvvvvv  */
243            mp_size_t size, i;
244            size = vsize - usize;
245            for (i = 0; i < size; i++)
246              tp[i] = ~vp[i] & GMP_NUMB_MASK;
247            cy_limb = mpn_sub_n (tp + size, up, vp + size, usize);
248            cy_limb+= mpn_sub_1 (tp + size, tp + size, usize, (mp_limb_t) 1);
249            cy_limb-= mpn_add_1 (tp, tp, vsize, (mp_limb_t) 1);
250            rsize = vsize;
251          }
252        if (cy_limb == 0)
253          {
254            tp[rsize] = 1;
255            rsize++;
256            exp++;
257          }
258        goto normalize;
259      }
260    }
261
262general_case:
263  /* If U extends beyond PREC, ignore the part that does.  */
264  if (usize > prec)
265    {
266      up += usize - prec;
267      usize = prec;
268    }
269
270  /* If V extends beyond PREC, ignore the part that does.
271     Note that this may make vsize negative.  */
272  if (vsize + ediff > prec)
273    {
274      vp += vsize + ediff - prec;
275      vsize = prec - ediff;
276    }
277
278  /* Allocate temp space for the result.  Allocate
279     just vsize + ediff later???  */
280  tp = (mp_ptr) TMP_ALLOC (prec * BYTES_PER_MP_LIMB);
281
282  if (ediff >= prec)
283    {
284      /* V completely cancelled.  */
285      if (tp != up)
286        MPN_COPY (rp, up, usize);
287      rsize = usize;
288    }
289  else
290    {
291      /* Locate the least significant non-zero limb in (the needed
292         parts of) U and V, to simplify the code below.  */
293      for (;;)
294        {
295          if (vsize == 0)
296            {
297              MPN_COPY (rp, up, usize);
298              rsize = usize;
299              goto done;
300            }
301          if (vp[0] != 0)
302            break;
303          vp++, vsize--;
304        }
305      for (;;)
306        {
307          if (usize == 0)
308            {
309              MPN_COPY (rp, vp, vsize);
310              rsize = vsize;
311              negate ^= 1;
312              goto done;
313            }
314          if (up[0] != 0)
315            break;
316          up++, usize--;
317        }
318
319      /* uuuu     |  uuuu     |  uuuu     |  uuuu     |  uuuu    */
320      /* vvvvvvv  |  vv       |    vvvvv  |    v      |       vv */
321
322      if (usize > ediff)
323        {
324          /* U and V partially overlaps.  */
325          if (ediff == 0)
326            {
327              /* Have to compare the leading limbs of u and v
328                 to determine whether to compute u - v or v - u.  */
329              if (usize >= vsize)
330                {
331                  /* uuuu     */
332                  /* vv       */
333                  mp_size_t size;
334                  size = usize - vsize;
335                  MPN_COPY (tp, up, size);
336                  mpn_sub_n (tp + size, up + size, vp, vsize);
337                  rsize = usize;
338                }
339              else /* (usize < vsize) */
340                {
341                  /* uuuu     */
342                  /* vvvvvvv  */
343                  mp_size_t size, i;
344                  size = vsize - usize;
345                  tp[0] = -vp[0] & GMP_NUMB_MASK;
346                  for (i = 1; i < size; i++)
347                    tp[i] = ~vp[i] & GMP_NUMB_MASK;
348                  mpn_sub_n (tp + size, up, vp + size, usize);
349                  mpn_sub_1 (tp + size, tp + size, usize, (mp_limb_t) 1);
350                  rsize = vsize;
351                }
352            }
353          else
354            {
355              if (vsize + ediff <= usize)
356                {
357                  /* uuuu     */
358                  /*   v      */
359                  mp_size_t size;
360                  size = usize - ediff - vsize;
361                  MPN_COPY (tp, up, size);
362                  mpn_sub (tp + size, up + size, usize - size, vp, vsize);
363                  rsize = usize;
364                }
365              else
366                {
367                  /* uuuu     */
368                  /*   vvvvv  */
369                  mp_size_t size, i;
370                  size = vsize + ediff - usize;
371                  tp[0] = -vp[0] & GMP_NUMB_MASK;
372                  for (i = 1; i < size; i++)
373                    tp[i] = ~vp[i] & GMP_NUMB_MASK;
374                  mpn_sub (tp + size, up, usize, vp + size, usize - ediff);
375                  mpn_sub_1 (tp + size, tp + size, usize, (mp_limb_t) 1);
376                  rsize = vsize + ediff;
377                }
378            }
379        }
380      else
381        {
382          /* uuuu     */
383          /*      vv  */
384          mp_size_t size, i;
385          size = vsize + ediff - usize;
386          tp[0] = -vp[0] & GMP_NUMB_MASK;
387          for (i = 1; i < vsize; i++)
388            tp[i] = ~vp[i] & GMP_NUMB_MASK;
389          for (i = vsize; i < size; i++)
390            tp[i] = GMP_NUMB_MAX;
391          mpn_sub_1 (tp + size, up, usize, (mp_limb_t) 1);
392          rsize = size + usize;
393        }
394
395    normalize:
396      /* Full normalize.  Optimize later.  */
397      while (rsize != 0 && tp[rsize - 1] == 0)
398        {
399          rsize--;
400          exp--;
401        }
402      MPN_COPY (rp, tp, rsize);
403    }
404
405 done:
406  r->_mp_size = negate ? -rsize : rsize;
407  if (rsize == 0)
408    exp = 0;
409  r->_mp_exp = exp;
410  TMP_FREE (marker);
411}
Note: See TracBrowser for help on using the repository browser.