xref: /PHP-5.5/ext/bcmath/libbcmath/src/doaddsub.c (revision c476bce2)
1 /* doaddsub.c: bcmath library file. */
2 /*
3     Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
4     Copyright (C) 2000 Philip A. Nelson
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.  (COPYING.LIB)
15 
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to:
18 
19       The Free Software Foundation, Inc.
20       59 Temple Place, Suite 330
21       Boston, MA 02111-1307 USA.
22 
23     You may contact the author by:
24        e-mail:  philnelson@acm.org
25       us-mail:  Philip A. Nelson
26                 Computer Science Department, 9062
27                 Western Washington University
28                 Bellingham, WA 98226-9062
29 
30 *************************************************************************/
31 
32 #include <config.h>
33 #include <stdio.h>
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include "bcmath.h"
39 #include "private.h"
40 
41 
42 /* Perform addition: N1 is added to N2 and the value is
43    returned.  The signs of N1 and N2 are ignored.
44    SCALE_MIN is to set the minimum scale of the result. */
45 
46  bc_num
_bc_do_add(n1,n2,scale_min)47 _bc_do_add (n1, n2, scale_min)
48      bc_num n1, n2;
49      int scale_min;
50 {
51   bc_num sum;
52   int sum_scale, sum_digits;
53   char *n1ptr, *n2ptr, *sumptr;
54   int carry, n1bytes, n2bytes;
55   int count;
56 
57   /* Prepare sum. */
58   sum_scale = MAX (n1->n_scale, n2->n_scale);
59   sum_digits = MAX (n1->n_len, n2->n_len) + 1;
60   sum = bc_new_num (sum_digits, MAX(sum_scale, scale_min));
61 
62   /* Zero extra digits made by scale_min. */
63   if (scale_min > sum_scale)
64     {
65       sumptr = (char *) (sum->n_value + sum_scale + sum_digits);
66       for (count = scale_min - sum_scale; count > 0; count--)
67 	*sumptr++ = 0;
68     }
69 
70   /* Start with the fraction part.  Initialize the pointers. */
71   n1bytes = n1->n_scale;
72   n2bytes = n2->n_scale;
73   n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1);
74   n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1);
75   sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1);
76 
77   /* Add the fraction part.  First copy the longer fraction.*/
78   if (n1bytes != n2bytes)
79     {
80       if (n1bytes > n2bytes)
81 	while (n1bytes>n2bytes)
82 	  { *sumptr-- = *n1ptr--; n1bytes--;}
83       else
84 	while (n2bytes>n1bytes)
85 	  { *sumptr-- = *n2ptr--; n2bytes--;}
86     }
87 
88   /* Now add the remaining fraction part and equal size integer parts. */
89   n1bytes += n1->n_len;
90   n2bytes += n2->n_len;
91   carry = 0;
92   while ((n1bytes > 0) && (n2bytes > 0))
93     {
94       *sumptr = *n1ptr-- + *n2ptr-- + carry;
95       if (*sumptr > (BASE-1))
96 	{
97 	   carry = 1;
98 	   *sumptr -= BASE;
99 	}
100       else
101 	carry = 0;
102       sumptr--;
103       n1bytes--;
104       n2bytes--;
105     }
106 
107   /* Now add carry the longer integer part. */
108   if (n1bytes == 0)
109     { n1bytes = n2bytes; n1ptr = n2ptr; }
110   while (n1bytes-- > 0)
111     {
112       *sumptr = *n1ptr-- + carry;
113       if (*sumptr > (BASE-1))
114 	{
115 	   carry = 1;
116 	   *sumptr -= BASE;
117 	 }
118       else
119 	carry = 0;
120       sumptr--;
121     }
122 
123   /* Set final carry. */
124   if (carry == 1)
125     *sumptr += 1;
126 
127   /* Adjust sum and return. */
128   _bc_rm_leading_zeros (sum);
129   return sum;
130 }
131 
132 
133 /* Perform subtraction: N2 is subtracted from N1 and the value is
134    returned.  The signs of N1 and N2 are ignored.  Also, N1 is
135    assumed to be larger than N2.  SCALE_MIN is the minimum scale
136    of the result. */
137 
138  bc_num
_bc_do_sub(n1,n2,scale_min)139 _bc_do_sub (n1, n2, scale_min)
140      bc_num n1, n2;
141      int scale_min;
142 {
143   bc_num diff;
144   int diff_scale, diff_len;
145   int min_scale, min_len;
146   char *n1ptr, *n2ptr, *diffptr;
147   int borrow, count, val;
148 
149   /* Allocate temporary storage. */
150   diff_len = MAX (n1->n_len, n2->n_len);
151   diff_scale = MAX (n1->n_scale, n2->n_scale);
152   min_len = MIN  (n1->n_len, n2->n_len);
153   min_scale = MIN (n1->n_scale, n2->n_scale);
154   diff = bc_new_num (diff_len, MAX(diff_scale, scale_min));
155 
156   /* Zero extra digits made by scale_min. */
157   if (scale_min > diff_scale)
158     {
159       diffptr = (char *) (diff->n_value + diff_len + diff_scale);
160       for (count = scale_min - diff_scale; count > 0; count--)
161 	*diffptr++ = 0;
162     }
163 
164   /* Initialize the subtract. */
165   n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1);
166   n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1);
167   diffptr = (char *) (diff->n_value + diff_len + diff_scale -1);
168 
169   /* Subtract the numbers. */
170   borrow = 0;
171 
172   /* Take care of the longer scaled number. */
173   if (n1->n_scale != min_scale)
174     {
175       /* n1 has the longer scale */
176       for (count = n1->n_scale - min_scale; count > 0; count--)
177 	*diffptr-- = *n1ptr--;
178     }
179   else
180     {
181       /* n2 has the longer scale */
182       for (count = n2->n_scale - min_scale; count > 0; count--)
183 	{
184 	  val = - *n2ptr-- - borrow;
185 	  if (val < 0)
186 	    {
187 	      val += BASE;
188 	      borrow = 1;
189 	    }
190 	  else
191 	    borrow = 0;
192 	  *diffptr-- = val;
193 	}
194     }
195 
196   /* Now do the equal length scale and integer parts. */
197 
198   for (count = 0; count < min_len + min_scale; count++)
199     {
200       val = *n1ptr-- - *n2ptr-- - borrow;
201       if (val < 0)
202 	{
203 	  val += BASE;
204 	  borrow = 1;
205 	}
206       else
207 	borrow = 0;
208       *diffptr-- = val;
209     }
210 
211   /* If n1 has more digits then n2, we now do that subtract. */
212   if (diff_len != min_len)
213     {
214       for (count = diff_len - min_len; count > 0; count--)
215 	{
216 	  val = *n1ptr-- - borrow;
217 	  if (val < 0)
218 	    {
219 	      val += BASE;
220 	      borrow = 1;
221 	    }
222 	  else
223 	    borrow = 0;
224 	  *diffptr-- = val;
225 	}
226     }
227 
228   /* Clean up and return. */
229   _bc_rm_leading_zeros (diff);
230   return diff;
231 }
232 
233