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