Skip to content

Commit f912fdf

Browse files
committed
create fast path and move old logic to standard path
1 parent 3600642 commit f912fdf

File tree

1 file changed

+108
-22
lines changed
  • ext/bcmath/libbcmath/src

1 file changed

+108
-22
lines changed

ext/bcmath/libbcmath/src/sqrt.c

Lines changed: 108 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,37 +32,89 @@
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;
45-
}
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;
42+
BC_VECTOR value = 1;
43+
while (exponent >= 8) {
44+
value *= BC_POW_10_LUT[8];
45+
exponent -= 8;
5146
}
47+
value *= BC_POW_10_LUT[exponent];
48+
return value;
49+
}
5250

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;
51+
static BC_VECTOR bc_fast_sqrt_vector(BC_VECTOR n_vector)
52+
{
53+
/* Use a bitwise method for approximating the square root
54+
* as the initial guess for Newton's method. */
55+
union {
56+
uint64_t i;
57+
double d;
58+
} u;
59+
u.d = (double) n_vector;
60+
u.i = (1ULL << 61) + (u.i >> 1) - (1ULL << 50);
61+
BC_VECTOR guess_vector = (BC_VECTOR) u.d;
62+
63+
/* Newton's algorithm. */
64+
BC_VECTOR guess1_vector;
65+
size_t diff;
66+
do {
67+
guess1_vector = guess_vector;
68+
guess_vector = n_vector / guess_vector;
69+
guess_vector += guess1_vector;
70+
guess_vector /= 2;
71+
diff = guess1_vector > guess_vector ? guess1_vector - guess_vector : guess_vector - guess1_vector;
72+
} while (diff > 1);
73+
return guess_vector;
74+
}
75+
76+
static inline void *bc_fast_sqrt(bc_num *num, size_t rscale)
77+
{
78+
BC_VECTOR n_vector = 0;
79+
size_t i = 0;
80+
for (; i < (*num)->n_len + (*num)->n_scale; i++) {
81+
n_vector = n_vector * BASE + (*num)->n_value[i];
5982
}
83+
/* When calculating the square root of a number using only integer operations,
84+
* need to adjust the digit scale accordingly.
85+
* Considering that the original number is the square of the result,
86+
* if the desired scale of the result is 5, the input number should be scaled
87+
* by twice that, i.e., scale 10. */
88+
n_vector *= bc_sqrt_get_pow_10((rscale + 1) * 2 - (*num)->n_scale);
89+
90+
/* Get sqrt */
91+
BC_VECTOR guess_vector = bc_fast_sqrt_vector(n_vector);
92+
93+
size_t full_len = 0;
94+
BC_VECTOR tmp_guess_vector = guess_vector;
95+
do {
96+
tmp_guess_vector /= BASE;
97+
full_len++;
98+
} while (tmp_guess_vector > 0);
99+
100+
size_t ret_ren = full_len > rscale + 1 ? full_len - (rscale + 1) : 1; /* for int zero */
101+
bc_num ret = bc_new_num_nonzeroed(ret_ren, rscale);
102+
char *rptr = ret->n_value;
103+
char *rend = rptr + ret_ren + rscale - 1;
104+
105+
guess_vector /= BASE; /* Since the scale of guess_vector is rscale + 1, reduce the scale by 1. */
106+
while (rend >= rptr) {
107+
*rend-- = guess_vector % BASE;
108+
guess_vector /= BASE;
109+
}
110+
bc_free_num(num);
111+
*num = ret;
112+
}
60113

61-
/* Initialize the variables. */
62-
size_t cscale;
114+
static inline void bc_standard_sqrt(bc_num *num, size_t rscale, bcmath_compare_result num_cmp_one)
115+
{
63116
bc_num guess;
64-
size_t rscale = MAX(scale, (*num)->n_scale);
65-
117+
size_t cscale;
66118
/* Calculate the initial guess. */
67119
if (num_cmp_one == BCMATH_RIGHT_GREATER) {
68120
/* The number is between 0 and 1. Guess should start at 1. */
@@ -84,7 +136,6 @@ bool bc_sqrt(bc_num *num, size_t scale)
84136
point5->n_value[1] = 5;
85137
bc_num diff = NULL;
86138

87-
/* Find the square root using Newton's algorithm. */
88139
bool done = false;
89140
while (!done) {
90141
bc_free_num (&guess1);
@@ -109,5 +160,40 @@ bool bc_sqrt(bc_num *num, size_t scale)
109160
bc_free_num (&guess1);
110161
bc_free_num (&point5);
111162
bc_free_num (&diff);
163+
}
164+
165+
bool bc_sqrt(bc_num *num, size_t scale)
166+
{
167+
/* Initial checks. */
168+
if (bc_is_neg(*num)) {
169+
/* Cannot take the square root of a negative number */
170+
return false;
171+
}
172+
/* Square root of 0 is 0 */
173+
if (bc_is_zero(*num)) {
174+
bc_free_num (num);
175+
*num = bc_copy_num(BCG(_zero_));
176+
return true;
177+
}
178+
179+
bcmath_compare_result num_cmp_one = bc_compare(*num, BCG(_one_), (*num)->n_scale);
180+
/* Square root of 1 is 1 */
181+
if (num_cmp_one == BCMATH_EQUAL) {
182+
bc_free_num (num);
183+
*num = bc_copy_num(BCG(_one_));
184+
return true;
185+
}
186+
187+
/* Initialize the variables. */
188+
size_t rscale = MAX(scale, (*num)->n_scale);
189+
size_t num_calc_full_len = (*num)->n_len + (rscale + 1) * 2;
190+
191+
/* Find the square root using Newton's algorithm. */
192+
if (num_calc_full_len < MAX_LENGTH_OF_LONG) {
193+
bc_fast_sqrt(num, rscale);
194+
} else {
195+
bc_standard_sqrt(num, rscale, num_cmp_one);
196+
}
197+
112198
return true;
113199
}

0 commit comments

Comments
 (0)