Skip to content

Commit 31d0c50

Browse files
committed
Omit low precision digits in calculations
1 parent 8d75231 commit 31d0c50

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
@@ -174,16 +174,25 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
174174
initial_guess %= BC_POW_10_LUT[guess_len_diff];
175175
guess_vector[guess_arr_size - 3] = initial_guess * BC_POW_10_LUT[BC_VECTOR_SIZE - guess_len_diff];
176176

177-
/* Initialize the uninitialized vector with zeros. */
177+
/* Initialize the uninitialized vector with `BC_VECTOR_BOUNDARY_NUM - 1`. */
178178
for (size_t i = 0; i < guess_arr_size - 3; i++) {
179-
guess_vector[i] = 0;
179+
guess_vector[i] = BC_VECTOR_BOUNDARY_NUM - 1;
180+
guess1_vector[i] = BC_VECTOR_BOUNDARY_NUM - 1;
180181
}
181182
guess_vector[guess_arr_size - 1] = 0;
182183

183-
size_t quot_size = n_arr_size - (guess_arr_size - 1) + 1;
184-
185184
BC_VECTOR two[1] = { 2 };
186185

186+
/* The precision (number of vectors) used for the calculation.
187+
* Since the initial value uses two vectors, the initial precision is set to 2. */
188+
size_t guess_precision = 2;
189+
size_t guess_offset = guess_arr_size - 1 - guess_precision;
190+
size_t n_offset = guess_offset * 2;
191+
size_t n_precision = n_arr_size - n_offset;
192+
size_t quot_size = n_precision - (guess_precision) + 1;
193+
size_t guess_use_len = guess_top_vector_len + BC_VECTOR_SIZE;
194+
bool updated_precision = false;
195+
187196
/**
188197
* Newton's algorithm. Iterative expression is `x_{n+1} = (x_n + a / x_n) / 2`
189198
* If break down the calculation into detailed steps, it looks like this:
@@ -194,16 +203,25 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
194203
*/
195204
bool done = false;
196205
do {
206+
if (updated_precision) {
207+
guess_offset = guess_arr_size - 1 - guess_precision;
208+
n_offset = guess_offset * 2;
209+
n_precision = n_arr_size - n_offset;
210+
quot_size = n_precision - (guess_precision) + 1;
211+
guess_use_len = guess_top_vector_len + (guess_precision - 1) * BC_VECTOR_SIZE;
212+
updated_precision = false;
213+
}
214+
197215
/* Since the value changes during division by successive approximation, use a copied version of it. */
198-
for (size_t i = 0; i < n_arr_size; i++) {
216+
for (size_t i = n_offset; i < n_arr_size; i++) {
199217
n_vector_copy[i] = n_vector[i];
200218
}
201219

202220
/* 1. quot = a / x_n */
203221
bool div_ret = bc_divide_vector(
204-
n_vector_copy, n_arr_size,
205-
guess_vector, guess_arr_size - 1, guess_full_len,
206-
tmp_div_ret_vector, quot_size
222+
n_vector_copy + n_offset, n_precision,
223+
guess_vector + guess_offset, guess_precision, guess_use_len,
224+
tmp_div_ret_vector + guess_offset, quot_size
207225
);
208226
ZEND_ASSERT(div_ret);
209227

@@ -213,7 +231,7 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
213231

214232
/* 2. add = x_n + quot1 */
215233
int carry = 0;
216-
for (size_t i = 0; i < guess_arr_size - 1; i++) {
234+
for (size_t i = guess_offset; i < guess_arr_size; i++) {
217235
guess_vector[i] = guess1_vector[i] + tmp_div_ret_vector[i] + carry;
218236
if (guess_vector[i] >= BC_VECTOR_BOUNDARY_NUM) {
219237
guess_vector[i] -= BC_VECTOR_BOUNDARY_NUM;
@@ -222,31 +240,42 @@ static inline void bc_standard_sqrt(bc_num *num, size_t rscale, size_t num_calc_
222240
carry = 0;
223241
}
224242
}
225-
guess_vector[guess_arr_size - 1] = carry;
243+
guess_vector[guess_arr_size - 1] = tmp_div_ret_vector[guess_arr_size - 1] + carry;
226244

227245
/* 3. x_{n+1} = add / 2 */
228246
div_ret = bc_divide_vector(
229-
guess_vector, guess_arr_size,
247+
guess_vector + guess_offset, guess_precision + 1,
230248
two, 1, 1,
231-
tmp_div_ret_vector, guess_arr_size
249+
tmp_div_ret_vector + guess_offset, guess_precision + 1
232250
);
233251
ZEND_ASSERT(div_ret);
234252

235-
for (size_t i = 0; i < guess_arr_size; i++) {
253+
for (size_t i = guess_offset; i < guess_arr_size; i++) {
236254
guess_vector[i] = tmp_div_ret_vector[i];
237255
}
256+
/* When estimating the square root of a number like 99,999,999, the result may sometimes have too many digits.
257+
* In such cases, the value is adjusted (corrected). */
258+
if (UNEXPECTED(tmp_div_ret_vector[guess_arr_size - 1] > 0)) {
259+
guess_vector[guess_arr_size - 1] = BC_VECTOR_BOUNDARY_NUM - 1;
260+
}
238261

239262
/* 4. repeat until the difference between the `x_n` and `x_{n+1}` is less than or equal to 1. */
240-
size_t diff = guess_vector[0] > guess1_vector[0] ? guess_vector[0] - guess1_vector[0] : guess1_vector[0] - guess_vector[0];
241-
if (diff <= 1) {
242-
bool is_same = true;
243-
for (size_t i = 1; i < guess_arr_size - 1; i++) {
244-
if (guess_vector[i] != guess1_vector[i]) {
245-
is_same = false;
246-
break;
263+
if (guess_precision < guess_arr_size - 1) {
264+
/* If the precision has not yet reached the maximum number of digits, it will be increased. */
265+
guess_precision = MIN(guess_precision * 2, guess_arr_size - 1);
266+
updated_precision = true;
267+
} else {
268+
size_t diff = guess_vector[0] > guess1_vector[0] ? guess_vector[0] - guess1_vector[0] : guess1_vector[0] - guess_vector[0];
269+
if (diff <= 1) {
270+
bool is_same = true;
271+
for (size_t i = 1; i < guess_arr_size - 1; i++) {
272+
if (guess_vector[i] != guess1_vector[i]) {
273+
is_same = false;
274+
break;
275+
}
247276
}
277+
done = is_same;
248278
}
249-
done = is_same;
250279
}
251280
} while (!done);
252281

0 commit comments

Comments
 (0)