xref: /php-src/ext/bcmath/libbcmath/src/doaddsub.c (revision e56ed6e1)
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.  (LICENSE)
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 "bcmath.h"
33 #include "private.h"
34 #include <stddef.h>
35 
36 
37 /* Perform addition: N1 is added to N2 and the value is
38    returned.  The signs of N1 and N2 are ignored.
39    SCALE_MIN is to set the minimum scale of the result. */
40 
_bc_do_add(bc_num n1,bc_num n2,size_t scale_min)41 bc_num _bc_do_add(bc_num n1, bc_num n2, size_t scale_min)
42 {
43 	bc_num sum;
44 	size_t sum_scale, sum_digits;
45 	char *n1ptr, *n2ptr, *sumptr;
46 	size_t n1bytes, n2bytes;
47 	bool carry;
48 
49 	/* Prepare sum. */
50 	sum_scale = MAX (n1->n_scale, n2->n_scale);
51 	sum_digits = MAX (n1->n_len, n2->n_len) + 1;
52 	sum = bc_new_num (sum_digits, MAX(sum_scale, scale_min));
53 
54 	/* Zero extra digits made by scale_min. */
55 	if (scale_min > sum_scale) {
56 		sumptr = (char *) (sum->n_value + sum_scale + sum_digits);
57 		for (int count = scale_min - sum_scale; count > 0; count--) {
58 			*sumptr++ = 0;
59 		}
60 	}
61 
62 	/* Start with the fraction part.  Initialize the pointers. */
63 	n1bytes = n1->n_scale;
64 	n2bytes = n2->n_scale;
65 	n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1);
66 	n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1);
67 	sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1);
68 
69 	/* Add the fraction part.  First copy the longer fraction.*/
70 	if (n1bytes != n2bytes) {
71 		if (n1bytes > n2bytes) {
72 			while (n1bytes > n2bytes) {
73 				*sumptr-- = *n1ptr--;
74 				n1bytes--;
75 			}
76 		} else {
77 			while (n2bytes > n1bytes) {
78 				*sumptr-- = *n2ptr--;
79 				n2bytes--;
80 			}
81 		}
82 	}
83 
84 	/* Now add the remaining fraction part and equal size integer parts. */
85 	n1bytes += n1->n_len;
86 	n2bytes += n2->n_len;
87 	carry = 0;
88 	while ((n1bytes > 0) && (n2bytes > 0)) {
89 		*sumptr = *n1ptr-- + *n2ptr-- + carry;
90 		if (*sumptr > (BASE - 1)) {
91 			carry = 1;
92 			*sumptr -= BASE;
93 		} else {
94 			carry = 0;
95 		}
96 		sumptr--;
97 		n1bytes--;
98 		n2bytes--;
99 	}
100 
101 	/* Now add carry the longer integer part. */
102 	if (n1bytes == 0) {
103 		n1bytes = n2bytes;
104 		n1ptr = n2ptr;
105 	}
106 	while (n1bytes-- > 0) {
107 		*sumptr = *n1ptr-- + carry;
108 		if (*sumptr > (BASE - 1)) {
109 			carry = true;
110 			*sumptr -= BASE;
111 		} else {
112 			carry = false;
113 		}
114 		sumptr--;
115 	}
116 
117 	/* Set final carry. */
118 	if (carry) {
119 		*sumptr += 1;
120 	}
121 
122 	/* Adjust sum and return. */
123 	_bc_rm_leading_zeros(sum);
124 	return sum;
125 }
126 
127 
128 /* Perform subtraction: N2 is subtracted from N1 and the value is
129    returned.  The signs of N1 and N2 are ignored.  Also, N1 is
130    assumed to be larger than N2.  SCALE_MIN is the minimum scale
131    of the result. */
_bc_do_sub(bc_num n1,bc_num n2,size_t scale_min)132 bc_num _bc_do_sub(bc_num n1, bc_num n2, size_t scale_min)
133 {
134 	bc_num diff;
135 	size_t diff_scale, diff_len;
136 	size_t min_scale, min_len;
137 	size_t borrow, count;
138 	int val;
139 	char *n1ptr, *n2ptr, *diffptr;
140 
141 	/* Allocate temporary storage. */
142 	diff_len = MAX(n1->n_len, n2->n_len);
143 	diff_scale = MAX(n1->n_scale, n2->n_scale);
144 	min_len = MIN(n1->n_len, n2->n_len);
145 	min_scale = MIN(n1->n_scale, n2->n_scale);
146 	diff = bc_new_num (diff_len, MAX(diff_scale, scale_min));
147 
148 	/* Zero extra digits made by scale_min. */
149 	if (scale_min > diff_scale) {
150 		diffptr = (char *) (diff->n_value + diff_len + diff_scale);
151 		for (count = scale_min - diff_scale; count > 0; count--) {
152 			*diffptr++ = 0;
153 		}
154 	}
155 
156 	/* Initialize the subtract. */
157 	n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale - 1);
158 	n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale - 1);
159 	diffptr = (char *) (diff->n_value + diff_len + diff_scale - 1);
160 
161 	/* Subtract the numbers. */
162 	borrow = 0;
163 
164 	/* Take care of the longer scaled number. */
165 	if (n1->n_scale != min_scale) {
166 		/* n1 has the longer scale */
167 		for (count = n1->n_scale - min_scale; count > 0; count--) {
168 			*diffptr-- = *n1ptr--;
169 		}
170 	} else {
171 		/* n2 has the longer scale */
172 		for (count = n2->n_scale - min_scale; count > 0; count--) {
173 			val = -*n2ptr-- - borrow;
174 			if (val < 0) {
175 				val += BASE;
176 				borrow = 1;
177 			} else {
178 				borrow = 0;
179 			}
180 			*diffptr-- = val;
181 		}
182 	}
183 
184 	/* Now do the equal length scale and integer parts. */
185 	for (count = 0; count < min_len + min_scale; count++) {
186 		val = *n1ptr-- - *n2ptr-- - borrow;
187 		if (val < 0) {
188 			val += BASE;
189 			borrow = 1;
190 		} else {
191 			borrow = 0;
192 		}
193 		*diffptr-- = val;
194 	}
195 
196 	/* If n1 has more digits than n2, we now do that subtract. */
197 	if (diff_len != min_len) {
198 		for (count = diff_len - min_len; count > 0; count--) {
199 			val = *n1ptr-- - borrow;
200 			if (val < 0) {
201 				val += BASE;
202 				borrow = 1;
203 			} else {
204 				borrow = 0;
205 			}
206 			*diffptr-- = val;
207 		}
208 	}
209 
210 	/* Clean up and return. */
211 	_bc_rm_leading_zeros(diff);
212 	return diff;
213 }
214