Skip to content

Commit 70ccad0

Browse files
committed
Omit low precision digits in calculations
1 parent 022d1fc commit 70ccad0

File tree

1 file changed

+50
-21
lines changed
  • ext/bcmath/libbcmath/src

1 file changed

+50
-21
lines changed

ext/bcmath/libbcmath/src/sqrt.c

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,25 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
167167
initial_guess %= BC_POW_10_LUT[guess_len_diff];
168168
guess_vector[guess_arr_size - 3] = initial_guess * BC_POW_10_LUT[BC_VECTOR_SIZE - guess_len_diff];
169169

170-
/* Initialize the uninitialized vector with zeros. */
170+
/* Initialize the uninitialized vector with `BC_VECTOR_BOUNDARY_NUM - 1`. */
171171
for (size_t i = 0; i < guess_arr_size - 3; i++) {
172-
guess_vector[i] = 0;
172+
guess_vector[i] = BC_VECTOR_BOUNDARY_NUM - 1;
173+
guess1_vector[i] = BC_VECTOR_BOUNDARY_NUM - 1;
173174
}
174175
guess_vector[guess_arr_size - 1] = 0;
175176

176-
size_t quot_size = n_arr_size - (guess_arr_size - 1) + 1;
177-
178177
BC_VECTOR two[1] = { 2 };
179178

179+
/* The precision (number of vectors) used for the calculation.
180+
* Since the initial value uses two vectors, the initial precision is set to 2. */
181+
size_t guess_precision = 2;
182+
size_t guess_offset = guess_arr_size - 1 - guess_precision;
183+
size_t n_offset = guess_offset * 2;
184+
size_t n_precision = n_arr_size - n_offset;
185+
size_t quot_size = n_precision - (guess_precision) + 1;
186+
size_t guess_use_len = guess_top_vector_len + BC_VECTOR_SIZE;
187+
bool updated_precision = false;
188+
180189
/**
181190
* Newton's algorithm. Iterative expression is `x_{n+1} = (x_n + a / x_n) / 2`
182191
* If break down the calculation into detailed steps, it looks like this:
@@ -187,16 +196,25 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
187196
*/
188197
bool done = false;
189198
do {
199+
if (updated_precision) {
200+
guess_offset = guess_arr_size - 1 - guess_precision;
201+
n_offset = guess_offset * 2;
202+
n_precision = n_arr_size - n_offset;
203+
quot_size = n_precision - (guess_precision) + 1;
204+
guess_use_len = guess_top_vector_len + (guess_precision - 1) * BC_VECTOR_SIZE;
205+
updated_precision = false;
206+
}
207+
190208
/* Since the value changes during division by successive approximation, use a copied version of it. */
191-
for (size_t i = 0; i < n_arr_size; i++) {
209+
for (size_t i = n_offset; i < n_arr_size; i++) {
192210
n_vector_copy[i] = n_vector[i];
193211
}
194212

195213
/* 1. quot = a / x_n */
196214
bc_divide_vector(
197-
n_vector_copy, n_arr_size,
198-
guess_vector, guess_arr_size - 1, guess_full_len,
199-
tmp_div_ret_vector, quot_size
215+
n_vector_copy + n_offset, n_precision,
216+
guess_vector + guess_offset, guess_precision, guess_use_len,
217+
tmp_div_ret_vector + guess_offset, quot_size
200218
);
201219

202220
BC_VECTOR *tmp_vptr = guess1_vector;
@@ -205,7 +223,7 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
205223

206224
/* 2. add = x_n + quot1 */
207225
int carry = 0;
208-
for (size_t i = 0; i < guess_arr_size - 1; i++) {
226+
for (size_t i = guess_offset; i < guess_arr_size; i++) {
209227
guess_vector[i] = guess1_vector[i] + tmp_div_ret_vector[i] + carry;
210228
if (guess_vector[i] >= BC_VECTOR_BOUNDARY_NUM) {
211229
guess_vector[i] -= BC_VECTOR_BOUNDARY_NUM;
@@ -214,30 +232,41 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
214232
carry = 0;
215233
}
216234
}
217-
guess_vector[guess_arr_size - 1] = carry;
235+
guess_vector[guess_arr_size - 1] = tmp_div_ret_vector[guess_arr_size - 1] + carry;
218236

219237
/* 3. x_{n+1} = add / 2 */
220238
bc_divide_vector(
221-
guess_vector, guess_arr_size,
239+
guess_vector + guess_offset, guess_precision + 1,
222240
two, 1, 1,
223-
tmp_div_ret_vector, guess_arr_size
241+
tmp_div_ret_vector + guess_offset, guess_precision + 1
224242
);
225243

226-
for (size_t i = 0; i < guess_arr_size; i++) {
244+
for (size_t i = guess_offset; i < guess_arr_size; i++) {
227245
guess_vector[i] = tmp_div_ret_vector[i];
228246
}
247+
/* When estimating the square root of a number like 99,999,999, the result may sometimes have too many digits.
248+
* In such cases, the value is adjusted (corrected). */
249+
if (UNEXPECTED(tmp_div_ret_vector[guess_arr_size - 1] > 0)) {
250+
guess_vector[guess_arr_size - 1] = BC_VECTOR_BOUNDARY_NUM - 1;
251+
}
229252

230253
/* 4. repeat until the difference between the `x_n` and `x_{n+1}` is less than or equal to 1. */
231-
size_t diff = guess_vector[0] > guess1_vector[0] ? guess_vector[0] - guess1_vector[0] : guess1_vector[0] - guess_vector[0];
232-
if (diff <= 1) {
233-
bool is_same = true;
234-
for (size_t i = 1; i < guess_arr_size - 1; i++) {
235-
if (guess_vector[i] != guess1_vector[i]) {
236-
is_same = false;
237-
break;
254+
if (guess_precision < guess_arr_size - 1) {
255+
/* If the precision has not yet reached the maximum number of digits, it will be increased. */
256+
guess_precision = MIN(guess_precision * 2, guess_arr_size - 1);
257+
updated_precision = true;
258+
} else {
259+
size_t diff = guess_vector[0] > guess1_vector[0] ? guess_vector[0] - guess1_vector[0] : guess1_vector[0] - guess_vector[0];
260+
if (diff <= 1) {
261+
bool is_same = true;
262+
for (size_t i = 1; i < guess_arr_size - 1; i++) {
263+
if (guess_vector[i] != guess1_vector[i]) {
264+
is_same = false;
265+
break;
266+
}
238267
}
268+
done = is_same;
239269
}
240-
done = is_same;
241270
}
242271
} while (!done);
243272

0 commit comments

Comments
 (0)