Skip to content

Commit 6d00b7b

Browse files
committed
optimized bc_raise
1 parent f303d7d commit 6d00b7b

File tree

1 file changed

+160
-31
lines changed

1 file changed

+160
-31
lines changed

ext/bcmath/libbcmath/src/raise.c

+160-31
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,149 @@
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 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+
#define bc_square_vector_ex(bvec, bsize, rvec) bc_multiply_vector_ex((bvec), (bsize), (*bvec), (bsize), (rvec))
59+
60+
/* Use "exponentiation by squaring". This is the fast path when the results are small. */
61+
static inline bc_num bc_fast_raise(
62+
const char *base_end, long exponent, size_t base_len, size_t power_len, size_t power_scale, size_t power_full_len)
63+
{
64+
BC_VECTOR base_vector = 0;
65+
66+
/* Convert to BC_VECTOR[] */
67+
bc_convert_to_vector(&base_vector, base_end, base_len);
68+
69+
while ((exponent & 1) == 0) {
70+
base_vector *= base_vector;
71+
exponent >>= 1;
72+
}
73+
74+
/* copy base to power */
75+
BC_VECTOR power_vector = base_vector;
76+
exponent >>= 1;
77+
78+
while (exponent > 0) {
79+
base_vector *= base_vector;
80+
if ((exponent & 1) == 1) {
81+
power_vector *= base_vector;
82+
}
83+
exponent >>= 1;
84+
}
85+
86+
bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
87+
char *pptr = power->n_value;
88+
char *pend = pptr + power_full_len - 1;
89+
90+
while (pend >= pptr) {
91+
*pend-- = power_vector % BASE;
92+
power_vector /= BASE;
93+
}
94+
return power;
95+
}
96+
97+
/* Use "exponentiation by squaring". This is the standard path. */
98+
static bc_num bc_standard_raise(
99+
const char *base_ptr, const char *base_end, long exponent, size_t base_len, size_t power_scale)
100+
{
101+
/* Remove the leading zeros as they will be filled in later. */
102+
while (*base_ptr++ == 0) {
103+
base_len--;
104+
}
105+
106+
size_t base_arr_size = BC_ARR_SIZE_FROM_LEN(base_len);
107+
size_t max_power_arr_size = base_arr_size * exponent;
108+
109+
/* The allocated memory area is reused on a rotational basis, so the same size is required. */
110+
BC_VECTOR *buf = safe_emalloc(max_power_arr_size * 3, sizeof(BC_VECTOR), 0);
111+
BC_VECTOR *base_vector = buf;
112+
BC_VECTOR *power_vector = base_vector + max_power_arr_size;
113+
BC_VECTOR *tmp_result_vector = power_vector + max_power_arr_size;
114+
115+
/* Convert to BC_VECTOR[] */
116+
bc_convert_to_vector(base_vector, base_end, base_len);
117+
118+
while ((exponent & 1) == 0) {
119+
base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
120+
exponent >>= 1;
121+
}
122+
123+
/* copy base to power */
124+
size_t power_arr_size = base_arr_size;
125+
for (size_t i = 0; i < base_arr_size; i++) {
126+
power_vector[i] = base_vector[i];
127+
}
128+
exponent >>= 1;
129+
130+
while (exponent > 0) {
131+
base_arr_size = bc_square_vector_ex(&base_vector, base_arr_size, &tmp_result_vector);
132+
if ((exponent & 1) == 1) {
133+
power_arr_size = bc_multiply_vector_ex(&power_vector, power_arr_size, base_vector, base_arr_size, &tmp_result_vector);
134+
}
135+
exponent >>= 1;
136+
}
137+
138+
/* Convert to bc_num */
139+
size_t power_leading_zeros = 0;
140+
size_t power_len;
141+
size_t power_full_len = power_arr_size * BC_VECTOR_SIZE;
142+
if (power_full_len > power_scale) {
143+
power_len = power_full_len - power_scale;
144+
} else {
145+
power_len = 1;
146+
power_leading_zeros = power_scale - power_full_len + 1;
147+
power_full_len = power_scale + 1;
148+
}
149+
bc_num power = bc_new_num_nonzeroed(power_len, power_scale);
150+
151+
char *pptr = power->n_value;
152+
char *pend = pptr + power_full_len - 1;
153+
154+
/* Pad with leading zeros if necessary. */
155+
while (power_leading_zeros > sizeof(uint32_t)) {
156+
bc_write_bcd_representation(0, pptr);
157+
pptr += sizeof(uint32_t);
158+
power_leading_zeros -= sizeof(uint32_t);
159+
}
160+
for (size_t i = 0; i < power_leading_zeros; i++) {
161+
*pptr++ = 0;
162+
}
163+
164+
bc_convert_vector_to_char(power_vector, pptr, pend, power_arr_size);
165+
166+
efree(buf);
167+
168+
return power;
41169
}
42170

43171
/* Raise "base" to the "exponent" power. The result is placed in RESULT.
44172
Maximum exponent is LONG_MAX. If a "exponent" is not an integer,
45173
only the integer part is used. */
46174
bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
47-
bc_num temp, power;
48175
size_t rscale;
49-
size_t pwrscale;
50-
size_t calcscale;
51176
bool is_neg;
52177

53178
/* Special case if exponent is a zero. */
@@ -74,43 +199,47 @@ bool bc_raise(bc_num base, long exponent, bc_num *result, size_t scale) {
74199
return !is_neg;
75200
}
76201

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;
202+
size_t base_len = base->n_len + base->n_scale;
203+
size_t power_len = base->n_len * exponent;
204+
size_t power_scale = base->n_scale * exponent;
205+
size_t power_full_len = power_len + power_scale;
206+
207+
sign power_sign;
208+
if (base->n_sign == MINUS && (exponent & 1) == 1) {
209+
power_sign = MINUS;
210+
} else {
211+
power_sign = PLUS;
84212
}
85-
temp = bc_copy_num(power);
86-
calcscale = pwrscale;
87-
exponent = exponent >> 1;
88213

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;
214+
const char *base_end = base->n_value + base_len - 1;
215+
216+
bc_num power;
217+
if (base_len <= BC_VECTOR_SIZE && power_full_len <= BC_VECTOR_SIZE * 2) {
218+
power = bc_fast_raise(base_end, exponent, base_len, power_len, power_scale, power_full_len);
219+
} else {
220+
power = bc_standard_raise(base->n_value, base_end, exponent, base_len, power_scale);
221+
}
222+
223+
_bc_rm_leading_zeros(power);
224+
if (bc_is_zero(power)) {
225+
power->n_sign = PLUS;
226+
power->n_scale = 0;
227+
} else {
228+
power->n_sign = power_sign;
98229
}
99230

100231
/* Assign the value. */
101232
if (is_neg) {
102-
if (bc_divide(BCG(_one_), temp, result, rscale) == false) {
103-
bc_free_num (&temp);
233+
if (bc_divide(BCG(_one_), power, result, rscale) == false) {
104234
bc_free_num (&power);
105235
return false;
106236
}
107-
bc_free_num (&temp);
237+
bc_free_num (&power);
108238
} else {
109239
bc_free_num (result);
110-
*result = temp;
240+
*result = power;
111241
(*result)->n_scale = MIN(scale, (*result)->n_scale);
112242
}
113-
bc_free_num (&power);
114243
return true;
115244
}
116245

0 commit comments

Comments
 (0)