Skip to content

Commit 0f72ae7

Browse files
committed
create fast path
1 parent 3600642 commit 0f72ae7

File tree

1 file changed

+115
-41
lines changed
  • ext/bcmath/libbcmath/src

1 file changed

+115
-41
lines changed

ext/bcmath/libbcmath/src/sqrt.c

Lines changed: 115 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
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. */
@@ -60,54 +61,127 @@ bool bc_sqrt(bc_num *num, size_t scale)
6061

6162
/* Initialize the variables. */
6263
size_t cscale;
63-
bc_num guess;
6464
size_t rscale = MAX(scale, (*num)->n_scale);
6565

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-
}
66+
/* Find the square root using Newton's algorithm. */
67+
if ((*num)->n_len + rscale + 1 < sizeof(LONG_MIN_DIGITS) - 1) {
68+
/* fast path */
69+
70+
/* Calculate the initial guess. */
71+
BC_VECTOR guess_vector = 1;
72+
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
73+
/* The number is between 0 and 1. Guess should start at 1. */
74+
cscale = (*num)->n_scale;
75+
} else {
76+
for (size_t i = 0; i < (*num)->n_len >> 1; i++) {
77+
guess_vector *= BASE;
78+
}
79+
cscale = 3;
80+
}
8181

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;
82+
BC_VECTOR n_vector = 0;
83+
size_t i = 0;
84+
for (; i < (*num)->n_len + (*num)->n_scale; i++) {
85+
n_vector = n_vector * BASE + (*num)->n_value[i];
86+
}
87+
for (; i < (*num)->n_len + rscale; i++) {
88+
n_vector *= BASE;
89+
}
8690

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

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);
112186
return true;
113187
}

0 commit comments

Comments
 (0)