Skip to content

Commit bbfe6d5

Browse files
committed
create fast path
1 parent 3600642 commit bbfe6d5

File tree

2 files changed

+108
-43
lines changed

2 files changed

+108
-43
lines changed

ext/bcmath/libbcmath/src/private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
*/
7676
#define BC_VECTOR_NO_OVERFLOW_ADD_COUNT (~((BC_VECTOR) 0) / (BC_VECTOR_BOUNDARY_NUM * BC_VECTOR_BOUNDARY_NUM))
7777

78-
static const BC_VECTOR BC_POW_10_LUT[9] = {
79-
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000
78+
static const BC_VECTOR BC_POW_10_LUT[11] = {
79+
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000
8080
};
8181

8282

ext/bcmath/libbcmath/src/sqrt.c

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,22 @@
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

40+
static inline BC_VECTOR bc_sqrt_get_pow_10(size_t exponent)
41+
{
42+
BC_VECTOR value = 1;
43+
while (exponent >= 10) {
44+
value *= BC_POW_10_LUT[10];
45+
exponent -= 10;
46+
}
47+
value *= BC_POW_10_LUT[exponent];
48+
return value;
49+
}
50+
3951
bool bc_sqrt(bc_num *num, size_t scale)
4052
{
4153
/* Initial checks. */
@@ -59,55 +71,108 @@ bool bc_sqrt(bc_num *num, size_t scale)
5971
}
6072

6173
/* Initialize the variables. */
62-
size_t cscale;
63-
bc_num guess;
6474
size_t rscale = MAX(scale, (*num)->n_scale);
6575

66-
/* Calculate the initial guess. */
67-
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
68-
/* The number is between 0 and 1. Guess should start at 1. */
69-
guess = bc_copy_num(BCG(_one_));
70-
cscale = (*num)->n_scale;
71-
} else {
72-
/* The number is greater than 1. Guess should start at 10^(exp/2). */
73-
/* If just divide size_t by 2 it will not overflow. */
74-
size_t exponent_for_initial_guess = (size_t) (*num)->n_len >> 1;
75-
76-
/* 10^n is a 1 followed by n zeros. */
77-
guess = bc_new_num(exponent_for_initial_guess + 1, 0);
78-
guess->n_value[0] = 1;
79-
cscale = 3;
80-
}
76+
/* Find the square root using Newton's algorithm. */
77+
if ((*num)->n_len + (rscale + 1) * 2 < sizeof(LONG_MIN_DIGITS) - 1) {
78+
/* fast path */
8179

82-
bc_num guess1 = NULL;
83-
bc_num point5 = bc_new_num (1, 1);
84-
point5->n_value[1] = 5;
85-
bc_num diff = NULL;
80+
/* Calculate the initial guess. */
81+
BC_VECTOR guess_vector = 1;
82+
if (num_cmp_one != BCMATH_RIGHT_GREATER) {
83+
guess_vector *= bc_sqrt_get_pow_10(((*num)->n_len + rscale + 1) >> 1);
84+
}
8685

87-
/* Find the square root using Newton's algorithm. */
88-
bool done = false;
89-
while (!done) {
90-
bc_free_num (&guess1);
91-
guess1 = bc_copy_num(guess);
92-
bc_divide(*num, guess, &guess, cscale);
93-
bc_add_ex(guess, guess1, &guess, 0);
94-
bc_multiply_ex(guess, point5, &guess, cscale);
95-
bc_sub_ex(guess, guess1, &diff, cscale + 1);
96-
if (bc_is_near_zero(diff, cscale)) {
97-
if (cscale < rscale + 1) {
98-
cscale = MIN (cscale * 3, rscale + 1);
99-
} else {
86+
BC_VECTOR n_vector = 0;
87+
size_t i = 0;
88+
for (; i < (*num)->n_len + (*num)->n_scale; i++) {
89+
n_vector = n_vector * BASE + (*num)->n_value[i];
90+
}
91+
n_vector *= bc_sqrt_get_pow_10((rscale + 1) * 2 - (*num)->n_scale);
92+
93+
BC_VECTOR guess1_vector;
94+
bool done = false;
95+
while (!done) {
96+
guess1_vector = guess_vector;
97+
guess_vector = n_vector / guess_vector;
98+
guess_vector += guess1_vector;
99+
guess_vector /= 2;
100+
size_t diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector;
101+
if (diff <= 1) {
100102
done = true;
101103
}
102104
}
105+
106+
size_t full_len = 0;
107+
BC_VECTOR tmp_guess_vector = guess_vector;
108+
do {
109+
tmp_guess_vector /= BASE;
110+
full_len++;
111+
} while (tmp_guess_vector > 0);
112+
113+
size_t ret_ren = full_len > rscale + 1 ? full_len - (rscale + 1) : 1; /* for int zero */
114+
bc_num ret = bc_new_num_nonzeroed(ret_ren, rscale);
115+
char *rptr = ret->n_value;
116+
char *rend = rptr + ret_ren + rscale - 1;
117+
118+
guess_vector /= BASE;
119+
while (rend >= rptr) {
120+
*rend-- = guess_vector % BASE;
121+
guess_vector /= BASE;
122+
}
123+
bc_free_num(num);
124+
*num = ret;
125+
} else {
126+
/* standard path */
127+
128+
bc_num guess;
129+
size_t cscale;
130+
/* Calculate the initial guess. */
131+
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
132+
/* The number is between 0 and 1. Guess should start at 1. */
133+
guess = bc_copy_num(BCG(_one_));
134+
cscale = (*num)->n_scale;
135+
} else {
136+
/* The number is greater than 1. Guess should start at 10^(exp/2). */
137+
/* If just divide size_t by 2 it will not overflow. */
138+
size_t exponent_for_initial_guess = (size_t) (*num)->n_len >> 1;
139+
140+
/* 10^n is a 1 followed by n zeros. */
141+
guess = bc_new_num(exponent_for_initial_guess + 1, 0);
142+
guess->n_value[0] = 1;
143+
cscale = 3;
144+
}
145+
146+
bc_num guess1 = NULL;
147+
bc_num point5 = bc_new_num (1, 1);
148+
point5->n_value[1] = 5;
149+
bc_num diff = NULL;
150+
151+
bool done = false;
152+
while (!done) {
153+
bc_free_num (&guess1);
154+
guess1 = bc_copy_num(guess);
155+
bc_divide(*num, guess, &guess, cscale);
156+
bc_add_ex(guess, guess1, &guess, 0);
157+
bc_multiply_ex(guess, point5, &guess, cscale);
158+
bc_sub_ex(guess, guess1, &diff, cscale + 1);
159+
if (bc_is_near_zero(diff, cscale)) {
160+
if (cscale < rscale + 1) {
161+
cscale = MIN (cscale * 3, rscale + 1);
162+
} else {
163+
done = true;
164+
}
165+
}
166+
}
167+
168+
/* Assign the number and clean up. */
169+
bc_free_num (num);
170+
bc_divide(guess, BCG(_one_), num, rscale);
171+
bc_free_num (&guess);
172+
bc_free_num (&guess1);
173+
bc_free_num (&point5);
174+
bc_free_num (&diff);
103175
}
104176

105-
/* Assign the number and clean up. */
106-
bc_free_num (num);
107-
bc_divide(guess, BCG(_one_), num, rscale);
108-
bc_free_num (&guess);
109-
bc_free_num (&guess1);
110-
bc_free_num (&point5);
111-
bc_free_num (&diff);
112177
return true;
113178
}

0 commit comments

Comments
 (0)