Skip to content

Commit 757e3bc

Browse files
committed
optimized bc_raise
1 parent f303d7d commit 757e3bc

File tree

1 file changed

+163
-31
lines changed

1 file changed

+163
-31
lines changed

ext/bcmath/libbcmath/src/raise.c

+163-31
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,152 @@
3030
*************************************************************************/
3131

3232
#include "bcmath.h"
33+
#include "convert.h"
34+
#include "private.h"
3335
#include <assert.h>
3436
#include <stdbool.h>
3537
#include <stddef.h>
3638

37-
void bc_square_ex(bc_num n1, bc_num *result, size_t scale_min) {
38-
bc_num square_ex = bc_square(n1, scale_min);
39-
bc_free_num(result);
40-
*(result) = square_ex;
39+
static inline size_t bc_multiply_vector_ex(
40+
BC_VECTOR **n1_vector, size_t n1_arr_size, BC_VECTOR *n2_vector, size_t n2_arr_size, BC_VECTOR **result_vector)
41+
{
42+
size_t result_arr_size = n1_arr_size + n2_arr_size;
43+
bc_multiply_vector(*n1_vector, n1_arr_size, n2_vector, n2_arr_size, *result_vector, result_arr_size);
44+
45+
/* Eliminate extra zeros because they increase the number of calculations. */
46+
while ((*result_vector)[result_arr_size - 1] == 0) {
47+
result_arr_size--;
48+
}
49+
50+
/* Swap n1_vector and result_vector. */
51+
BC_VECTOR *tmp = *n1_vector;
52+
*n1_vector = *result_vector;
53+
*result_vector = tmp;
54+
55+
return result_arr_size;
56+
}
57+
58+
static inline size_t bc_square_vector_ex(BC_VECTOR **base_vector, size_t base_arr_size, BC_VECTOR **result_vector)
59+
{
60+
return bc_multiply_vector_ex(base_vector, base_arr_size, *base_vector, base_arr_size, result_vector);
61+
}
62+
63+
/* Use "exponentiation by squaring". This is the fast path when the results are small. */
64+
static inline bc_num bc_fast_raise(
65+
const char *base_end, long exponent, size_t base_len, size_t power_len, size_t power_scale, size_t power_full_len)
66+
{
67+
BC_VECTOR base_vector = 0;
68+
69+
/* Convert to BC_VECTOR[] */
70+
bc_convert_to_vector(&base_vector, base_end, base_len);
71+
72+
while ((exponent & 1) == 0) {
73+
base_vector *= base_vector;
74+
exponent >>= 1;
75+
}
76+
77+
/* copy base to power */
78+
BC_VECTOR power_vector = base_vector;
79+
exponent >>= 1;
80+
81+
while (exponent > 0) {
82+
base_vector *= base_vector;
83+
if ((exponent & 1) == 1) {
84+
power_vector *= base_vector;
85+
}
86+
exponent >>= 1;
87+
}
88+
89+
bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
90+
char *pptr = power->n_value;
91+
char *pend = pptr + power_full_len - 1;
92+
93+
while (pend >= pptr) {
94+
*pend-- = power_vector % BASE;
95+
power_vector /= BASE;
96+
}
97+
return power;
98+
}
99+
100+
/* Use "exponentiation by squaring". This is the standard path. */
101+
static bc_num bc_standard_raise(
102+
const char *base_ptr, const char *base_end, long exponent, size_t base_len, size_t power_scale)
103+
{
104+
/* Remove the leading zeros as they will be filled in later. */
105+
while (*base_ptr++ == 0) {
106+
base_len--;
107+
}
108+
109+
size_t base_arr_size = BC_ARR_SIZE_FROM_LEN(base_len);
110+
size_t max_power_arr_size = base_arr_size * exponent;
111+
112+
/* The allocated memory area is reused on a rotational basis, so the same size is required. */
113+
BC_VECTOR *buf = safe_emalloc(max_power_arr_size * 3, sizeof(BC_VECTOR), 0);
114+
BC_VECTOR *base_vector = buf;
115+
BC_VECTOR *power_vector = base_vector + max_power_arr_size;
116+
BC_VECTOR *tmp_result_vector = power_vector + max_power_arr_size;
117+
118+
/* Convert to BC_VECTOR[] */
119+
bc_convert_to_vector(base_vector, base_end, base_len);
120+
121+
while ((exponent & 1) == 0) {
122+
base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
123+
exponent >>= 1;
124+
}
125+
126+
/* copy base to power */
127+
size_t power_arr_size = base_arr_size;
128+
for (size_t i = 0; i < base_arr_size; i++) {
129+
power_vector[i] = base_vector[i];
130+
}
131+
exponent >>= 1;
132+
133+
while (exponent > 0) {
134+
base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
135+
if ((exponent & 1) == 1) {
136+
power_arr_size = bc_multiply_vector_ex(&power_vector, power_arr_size, base_vector, base_arr_size, &tmp_result_vector);
137+
}
138+
exponent >>= 1;
139+
}
140+
141+
/* Convert to bc_num */
142+
size_t power_leading_zeros = 0;
143+
size_t power_len;
144+
size_t power_full_len = power_arr_size * BC_VECTOR_SIZE;
145+
if (power_full_len > power_scale) {
146+
power_len = power_full_len - power_scale;
147+
} else {
148+
power_len = 1;
149+
power_leading_zeros = power_scale - power_full_len + 1;
150+
power_full_len = power_scale + 1;
151+
}
152+
bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
153+
154+
char *pptr = power->n_value;
155+
char *pend = pptr + power_full_len - 1;
156+
157+
/* Pad with leading zeros if necessary. */
158+
while (power_leading_zeros > sizeof(uint32_t)) {
159+
bc_write_bcd_representation(0, pptr);
160+
pptr += sizeof(uint32_t);
161+
power_leading_zeros -= sizeof(uint32_t);
162+
}
163+
for (size_t i = 0; i < power_leading_zeros; i++) {
164+
*pptr++ = 0;
165+
}
166+
167+
bc_convert_vector_to_char(power_vector, pptr, pend, power_arr_size);
168+
169+
efree(buf);
170+
171+
return power;
41172
}
42173

43174
/* Raise "base" to the "exponent" power. The result is placed in RESULT.
44175
Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
45176
only the integer part is used. */
46177
bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
47-
bc_num temp, power;
48178
size_t rscale;
49-
size_t pwrscale;
50-
size_t calcscale;
51179
bool is_neg;
52180

53181
/* Special case if exponent is a zero. */
@@ -74,43 +202,47 @@ bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
74202
return !is_neg;
75203
}
76204

77-
/* Set initial value of temp. */
78-
power = bc_copy_num(base);
79-
pwrscale = base->n_scale;
80-
while ((exponent & 1) == 0) {
81-
pwrscale = 2 * pwrscale;
82-
bc_square_ex(power, &power, pwrscale);
83-
exponent = exponent >> 1;
205+
size_t base_len = base->n_len + base->n_scale;
206+
size_t power_len = base->n_len * exponent;
207+
size_t power_scale = base->n_scale * exponent;
208+
size_t power_full_len = power_len + power_scale;
209+
210+
sign power_sign;
211+
if (base->n_sign == MINUS && (exponent & 1) == 1) {
212+
power_sign = MINUS;
213+
} else {
214+
power_sign = PLUS;
84215
}
85-
temp = bc_copy_num(power);
86-
calcscale = pwrscale;
87-
exponent = exponent >> 1;
88216

89-
/* Do the calculation. */
90-
while (exponent > 0) {
91-
pwrscale = 2 * pwrscale;
92-
bc_square_ex(power, &power, pwrscale);
93-
if ((exponent & 1) == 1) {
94-
calcscale = pwrscale + calcscale;
95-
bc_multiply_ex(temp, power, &temp, calcscale);
96-
}
97-
exponent = exponent >> 1;
217+
const char *base_end = base->n_value + base_len - 1;
218+
219+
bc_num power;
220+
if (base_len <= BC_VECTOR_SIZE && power_full_len <= BC_VECTOR_SIZE * 2) {
221+
power = bc_fast_raise(base_end, exponent, base_len, power_len, power_scale, power_full_len);
222+
} else {
223+
power = bc_standard_raise(base->n_value, base_end, exponent, base_len, power_scale);
224+
}
225+
226+
_bc_rm_leading_zeros(power);
227+
if (bc_is_zero(power)) {
228+
power->n_sign = PLUS;
229+
power->n_scale = 0;
230+
} else {
231+
power->n_sign = power_sign;
98232
}
99233

100234
/* Assign the value. */
101235
if (is_neg) {
102-
if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
103-
bc_free_num (&temp);
236+
if (bc_divide(BCG(_one_), power, result, rscale) == false) {
104237
bc_free_num (&power);
105238
return false;
106239
}
107-
bc_free_num (&temp);
240+
bc_free_num (&power);
108241
} else {
109242
bc_free_num (result);
110-
*result = temp;
243+
*result = power;
111244
(*result)->n_scale = MIN(scale, (*result)->n_scale);
112245
}
113-
bc_free_num (&power);
114246
return true;
115247
}
116248

0 commit comments

Comments
 (0)