Skip to content

Commit 8a5da50

Browse files
committed
create fast path and move old logic to standard path
1 parent 22b6adb commit 8a5da50

File tree

1 file changed

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

1 file changed

+109
-21
lines changed

ext/bcmath/libbcmath/src/sqrt.c

Lines changed: 109 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,78 @@
3232
#include "bcmath.h"
3333
#include <stdbool.h>
3434
#include <stddef.h>
35+
#include "private.h"
3536

3637
/* Take the square root NUM and return it in NUM with SCALE digits
3738
after the decimal place. */
3839

39-
bool bc_sqrt(bc_num *num, size_t scale)
40+
static inline BC_VECTOR bc_sqrt_get_pow_10(size_t exponent)
4041
{
41-
/* Initial checks. */
42-
if (bc_is_neg(*num)) {
43-
/* Cannot take the square root of a negative number */
44-
return false;
42+
BC_VECTOR value = 1;
43+
while (exponent >= 8) {
44+
value *= BC_POW_10_LUT[8];
45+
exponent -= 8;
4546
}
46-
/* Square root of 0 is 0 */
47-
if (bc_is_zero(*num)) {
48-
bc_free_num (num);
49-
*num = bc_copy_num(BCG(_zero_));
50-
return true;
47+
value *= BC_POW_10_LUT[exponent];
48+
return value;
49+
}
50+
51+
static BC_VECTOR bc_fast_sqrt_vector(BC_VECTOR n_vector)
52+
{
53+
/* Use sqrt() from <math.h> to determine the initial value. */
54+
BC_VECTOR guess_vector = (BC_VECTOR) round(sqrt((double) n_vector));
55+
56+
/* Newton's algorithm. Iterative expression is `x_{n+1} = (x_n + a / x_n) / 2` */
57+
BC_VECTOR guess1_vector;
58+
size_t diff;
59+
do {
60+
guess1_vector = guess_vector;
61+
guess_vector = (guess1_vector + n_vector / guess1_vector) / 2; /* Iterative expression */
62+
diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector;
63+
} while (diff > 1);
64+
return guess_vector;
65+
}
66+
67+
static inline void bc_fast_sqrt(bc_num *num, size_t rscale, size_t num_calc_full_len, size_t leading_zeros)
68+
{
69+
BC_VECTOR n_vector = 0;
70+
for (size_t i = leading_zeros; i < (*num)->n_len + (*num)->n_scale; i++) {
71+
n_vector = n_vector * BASE + (*num)->n_value[i];
72+
}
73+
/* When calculating the square root of a number using only integer operations,
74+
* need to adjust the digit scale accordingly.
75+
* Considering that the original number is the square of the result,
76+
* if the desired scale of the result is 5, the input number should be scaled
77+
* by twice that, i.e., scale 10. */
78+
n_vector *= bc_sqrt_get_pow_10((rscale + 1) * 2 - (*num)->n_scale);
79+
80+
/* Get sqrt */
81+
BC_VECTOR guess_vector = bc_fast_sqrt_vector(n_vector);
82+
83+
size_t ret_len;
84+
if (leading_zeros > 0) {
85+
ret_len = 1;
86+
} else {
87+
ret_len = ((num_calc_full_len + 1) / 2) - (rscale + 1);
5188
}
5289

53-
bcmath_compare_result num_cmp_one = bc_compare(*num, BCG(_one_), (*num)->n_scale);
54-
/* Square root of 1 is 1 */
55-
if (num_cmp_one == BCMATH_EQUAL) {
56-
bc_free_num (num);
57-
*num = bc_copy_num(BCG(_one_));
58-
return true;
90+
bc_num ret = bc_new_num_nonzeroed(ret_len, rscale);
91+
char *rptr = ret->n_value;
92+
char *rend = rptr + ret_len + rscale - 1;
93+
94+
guess_vector /= BASE; /* Since the scale of guess_vector is rscale + 1, reduce the scale by 1. */
95+
while (rend >= rptr) {
96+
*rend-- = guess_vector % BASE;
97+
guess_vector /= BASE;
5998
}
99+
bc_free_num(num);
100+
*num = ret;
101+
}
60102

61-
/* Initialize the variables. */
62-
size_t cscale;
103+
static inline void bc_standard_sqrt(bc_num *num, size_t rscale, bcmath_compare_result num_cmp_one)
104+
{
63105
bc_num guess;
64-
size_t rscale = MAX(scale, (*num)->n_scale);
65-
106+
size_t cscale;
66107
/* Calculate the initial guess. */
67108
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
68109
/* The number is between 0 and 1. Guess should start at 1. */
@@ -86,7 +127,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
86127
point5->n_value[1] = 5;
87128
bc_num diff = NULL;
88129

89-
/* Find the square root using Newton's algorithm. */
90130
bool done = false;
91131
while (!done) {
92132
bc_free_num (&guess1);
@@ -111,5 +151,53 @@ bool bc_sqrt(bc_num *num, size_t scale)
111151
bc_free_num (&guess1);
112152
bc_free_num (&point5);
113153
bc_free_num (&diff);
154+
}
155+
156+
bool bc_sqrt(bc_num *num, size_t scale)
157+
{
158+
/* Initial checks. */
159+
if (bc_is_neg(*num)) {
160+
/* Cannot take the square root of a negative number */
161+
return false;
162+
}
163+
/* Square root of 0 is 0 */
164+
if (bc_is_zero(*num)) {
165+
bc_free_num (num);
166+
*num = bc_copy_num(BCG(_zero_));
167+
return true;
168+
}
169+
170+
bcmath_compare_result num_cmp_one = bc_compare(*num, BCG(_one_), (*num)->n_scale);
171+
/* Square root of 1 is 1 */
172+
if (num_cmp_one == BCMATH_EQUAL) {
173+
bc_free_num (num);
174+
*num = bc_copy_num(BCG(_one_));
175+
return true;
176+
}
177+
178+
/* Initialize the variables. */
179+
size_t rscale = MAX(scale, (*num)->n_scale);
180+
size_t leading_zeros = 0;
181+
size_t num_calc_full_len;
182+
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
183+
const char *nptr = (*num)->n_value;
184+
/* We know that num is not zero, so there will be no overrun. */
185+
while (*nptr == 0) {
186+
leading_zeros++;
187+
nptr++;
188+
}
189+
size_t leading_zeros_in_frac = leading_zeros - 1;
190+
num_calc_full_len = (rscale + 1 - leading_zeros_in_frac) * 2;
191+
} else {
192+
num_calc_full_len = (*num)->n_len + (rscale + 1) * 2;
193+
}
194+
195+
/* Find the square root using Newton's algorithm. */
196+
if (num_calc_full_len < MAX_LENGTH_OF_LONG) {
197+
bc_fast_sqrt(num, rscale, num_calc_full_len, leading_zeros);
198+
} else {
199+
bc_standard_sqrt(num, rscale, num_cmp_one);
200+
}
201+
114202
return true;
115203
}

0 commit comments

Comments
 (0)