xref: /dragonfly/contrib/gmp/mpf/ui_sub.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1 /* mpf_ui_sub -- Subtract a float from an unsigned long int.
2 
3 Copyright 1993, 1994, 1995, 1996, 2001, 2002, 2005 Free Software Foundation,
4 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
mpf_ui_sub(mpf_ptr r,unsigned long int u,mpf_srcptr v)25 mpf_ui_sub (mpf_ptr r, unsigned long int 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 uexp;
32   mp_size_t ediff;
33   int negate;
34   mp_limb_t ulimb;
35   TMP_DECL;
36 
37   vsize = v->_mp_size;
38 
39   /* Handle special cases that don't work in generic code below.  */
40   if (u == 0)
41     {
42       mpf_neg (r, v);
43       return;
44     }
45   if (vsize == 0)
46     {
47       mpf_set_ui (r, u);
48       return;
49     }
50 
51   /* If signs of U and V are different, perform addition.  */
52   if (vsize < 0)
53     {
54       __mpf_struct v_negated;
55       v_negated._mp_size = -vsize;
56       v_negated._mp_exp = v->_mp_exp;
57       v_negated._mp_d = v->_mp_d;
58       mpf_add_ui (r, &v_negated, u);
59       return;
60     }
61 
62   TMP_MARK;
63 
64   /* Signs are now known to be the same.  */
65 
66   ulimb = u;
67   /* Make U be the operand with the largest exponent.  */
68   if (1 < v->_mp_exp)
69     {
70       negate = 1;
71       usize = ABS (vsize);
72       vsize = 1;
73       up = v->_mp_d;
74       vp = &ulimb;
75       rp = r->_mp_d;
76       prec = r->_mp_prec + 1;
77       uexp = v->_mp_exp;
78       ediff = uexp - 1;
79     }
80   else
81     {
82       negate = 0;
83       usize = 1;
84       vsize = ABS (vsize);
85       up = &ulimb;
86       vp = v->_mp_d;
87       rp = r->_mp_d;
88       prec = r->_mp_prec;
89       uexp = 1;
90       ediff = 1 - v->_mp_exp;
91     }
92 
93   /* Ignore leading limbs in U and V that are equal.  Doing
94      this helps increase the precision of the result.  */
95   if (ediff == 0)
96     {
97       /* This loop normally exits immediately.  Optimize for that.  */
98       for (;;)
99           {
100             usize--;
101             vsize--;
102             if (up[usize] != vp[vsize])
103               break;
104             uexp--;
105             if (usize == 0)
106               goto Lu0;
107             if (vsize == 0)
108               goto Lv0;
109           }
110       usize++;
111       vsize++;
112       /* Note that either operand (but not both operands) might now have
113            leading zero limbs.  It matters only that U is unnormalized if
114            vsize is now zero, and vice versa.  And it is only in that case
115            that we have to adjust uexp.  */
116       if (vsize == 0)
117       Lv0:
118           while (usize != 0 && up[usize - 1] == 0)
119             usize--, uexp--;
120       if (usize == 0)
121       Lu0:
122           while (vsize != 0 && vp[vsize - 1] == 0)
123             vsize--, uexp--;
124     }
125 
126   /* If U extends beyond PREC, ignore the part that does.  */
127   if (usize > prec)
128     {
129       up += usize - prec;
130       usize = prec;
131     }
132 
133   /* If V extends beyond PREC, ignore the part that does.
134      Note that this may make vsize negative.  */
135   if (vsize + ediff > prec)
136     {
137       vp += vsize + ediff - prec;
138       vsize = prec - ediff;
139     }
140 
141   /* Allocate temp space for the result.  Allocate
142      just vsize + ediff later???  */
143   tp = TMP_ALLOC_LIMBS (prec);
144 
145   if (ediff >= prec)
146     {
147       /* V completely cancelled.  */
148       if (tp != up)
149           MPN_COPY (rp, up, usize);
150       rsize = usize;
151     }
152   else
153     {
154       /* Locate the least significant non-zero limb in (the needed
155            parts of) U and V, to simplify the code below.  */
156       for (;;)
157           {
158             if (vsize == 0)
159               {
160                 MPN_COPY (rp, up, usize);
161                 rsize = usize;
162                 goto done;
163               }
164             if (vp[0] != 0)
165               break;
166             vp++, vsize--;
167           }
168       for (;;)
169           {
170             if (usize == 0)
171               {
172                 MPN_COPY (rp, vp, vsize);
173                 rsize = vsize;
174                 negate ^= 1;
175                 goto done;
176               }
177             if (up[0] != 0)
178               break;
179             up++, usize--;
180           }
181 
182       /* uuuu     |  uuuu     |  uuuu     |  uuuu     |  uuuu    */
183       /* vvvvvvv  |  vv       |    vvvvv  |    v      |       vv */
184 
185       if (usize > ediff)
186           {
187             /* U and V partially overlaps.  */
188             if (ediff == 0)
189               {
190                 /* Have to compare the leading limbs of u and v
191                      to determine whether to compute u - v or v - u.  */
192                 if (usize > vsize)
193                     {
194                       /* uuuu     */
195                       /* vv       */
196                       int cmp;
197                       cmp = mpn_cmp (up + usize - vsize, vp, vsize);
198                       if (cmp >= 0)
199                         {
200                           mp_size_t size;
201                           size = usize - vsize;
202                           MPN_COPY (tp, up, size);
203                           mpn_sub_n (tp + size, up + size, vp, vsize);
204                           rsize = usize;
205                         }
206                       else
207                         {
208                           /* vv       */  /* Swap U and V. */
209                           /* uuuu     */
210                           mp_size_t size, i;
211                           size = usize - vsize;
212                           tp[0] = -up[0] & GMP_NUMB_MASK;
213                           for (i = 1; i < size; i++)
214                               tp[i] = ~up[i] & GMP_NUMB_MASK;
215                           mpn_sub_n (tp + size, vp, up + size, vsize);
216                           mpn_sub_1 (tp + size, tp + size, vsize, (mp_limb_t) 1);
217                           negate ^= 1;
218                           rsize = usize;
219                         }
220                     }
221                 else if (usize < vsize)
222                     {
223                       /* uuuu     */
224                       /* vvvvvvv  */
225                       int cmp;
226                       cmp = mpn_cmp (up, vp + vsize - usize, usize);
227                       if (cmp > 0)
228                         {
229                           mp_size_t size, i;
230                           size = vsize - usize;
231                           tp[0] = -vp[0] & GMP_NUMB_MASK;
232                           for (i = 1; i < size; i++)
233                               tp[i] = ~vp[i] & GMP_NUMB_MASK;
234                           mpn_sub_n (tp + size, up, vp + size, usize);
235                           mpn_sub_1 (tp + size, tp + size, usize, (mp_limb_t) 1);
236                           rsize = vsize;
237                         }
238                       else
239                         {
240                           /* vvvvvvv  */  /* Swap U and V. */
241                           /* uuuu     */
242                           /* This is the only place we can get 0.0.  */
243                           mp_size_t size;
244                           size = vsize - usize;
245                           MPN_COPY (tp, vp, size);
246                           mpn_sub_n (tp + size, vp + size, up, usize);
247                           negate ^= 1;
248                           rsize = vsize;
249                         }
250                     }
251                 else
252                     {
253                       /* uuuu     */
254                       /* vvvv     */
255                       int cmp;
256                       cmp = mpn_cmp (up, vp + vsize - usize, usize);
257                       if (cmp > 0)
258                         {
259                           mpn_sub_n (tp, up, vp, usize);
260                           rsize = usize;
261                         }
262                       else
263                         {
264                           mpn_sub_n (tp, vp, up, usize);
265                           negate ^= 1;
266                           rsize = usize;
267                           /* can give zero */
268                         }
269                     }
270               }
271             else
272               {
273                 if (vsize + ediff <= usize)
274                     {
275                       /* uuuu     */
276                       /*   v      */
277                       mp_size_t size;
278                       size = usize - ediff - vsize;
279                       MPN_COPY (tp, up, size);
280                       mpn_sub (tp + size, up + size, usize - size, vp, vsize);
281                       rsize = usize;
282                     }
283                 else
284                     {
285                       /* uuuu     */
286                       /*   vvvvv  */
287                       mp_size_t size, i;
288                       size = vsize + ediff - usize;
289                       tp[0] = -vp[0] & GMP_NUMB_MASK;
290                       for (i = 1; i < size; i++)
291                         tp[i] = ~vp[i] & GMP_NUMB_MASK;
292                       mpn_sub (tp + size, up, usize, vp + size, usize - ediff);
293                       mpn_sub_1 (tp + size, tp + size, usize, (mp_limb_t) 1);
294                       rsize = vsize + ediff;
295                     }
296               }
297           }
298       else
299           {
300             /* uuuu     */
301             /*      vv  */
302             mp_size_t size, i;
303             size = vsize + ediff - usize;
304             tp[0] = -vp[0] & GMP_NUMB_MASK;
305             for (i = 1; i < vsize; i++)
306               tp[i] = ~vp[i] & GMP_NUMB_MASK;
307             for (i = vsize; i < size; i++)
308               tp[i] = GMP_NUMB_MAX;
309             mpn_sub_1 (tp + size, up, usize, (mp_limb_t) 1);
310             rsize = size + usize;
311           }
312 
313       /* Full normalize.  Optimize later.  */
314       while (rsize != 0 && tp[rsize - 1] == 0)
315           {
316             rsize--;
317             uexp--;
318           }
319       MPN_COPY (rp, tp, rsize);
320     }
321 
322  done:
323   r->_mp_size = negate ? -rsize : rsize;
324   r->_mp_exp = uexp;
325   TMP_FREE;
326 }
327