diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index f53032fabc35c..d3029b96d0f5d 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -541,7 +541,11 @@ PHP_FUNCTION(bcpow) goto cleanup; } - bc_raise(first, exponent, &result, scale); + if(exponent == 2) { + bc_square(first, &result, scale); + } else { + bc_raise(first, exponent, &result, scale); + } RETVAL_STR(bc_num2str_ex(result, scale)); diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h index de51ee7457110..5e33b34fa5e67 100644 --- a/ext/bcmath/libbcmath/src/bcmath.h +++ b/ext/bcmath/libbcmath/src/bcmath.h @@ -119,6 +119,8 @@ void bc_sub(bc_num n1, bc_num n2, bc_num *result, size_t scale_min); void bc_multiply(bc_num n1, bc_num n2, bc_num *prod, size_t scale); +void bc_square(bc_num num, bc_num *prod, size_t scale); + bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, int scale); bool bc_modulo(bc_num num1, bc_num num2, bc_num *resul, size_t scale); diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c index f45188455e7f2..debc336315da9 100644 --- a/ext/bcmath/libbcmath/src/div.c +++ b/ext/bcmath/libbcmath/src/div.c @@ -39,12 +39,12 @@ /* Some utility routines for the divide: First a one digit multiply. NUM (with SIZE digits) is multiplied by DIGIT and the result is - placed into RESULT. It is written so that NUM and RESULT can be + placed into RESULT. It is written so that NUM and RESULT can be the same pointers. */ static void _one_mult(unsigned char *num, size_t size, int digit, unsigned char *result) { - size_t carry, value; + int carry, value; unsigned char *nptr, *rptr; if (digit == 0) { @@ -71,7 +71,6 @@ static void _one_mult(unsigned char *num, size_t size, int digit, unsigned char } } - /* The full division routine. This computes N1 / N2. It returns true if the division is ok and the result is in QUOT. The number of digits after the decimal point is SCALE. It returns false if division diff --git a/ext/bcmath/libbcmath/src/raise.c b/ext/bcmath/libbcmath/src/raise.c index 61d64ace94744..969b3b5ac89ae 100644 --- a/ext/bcmath/libbcmath/src/raise.c +++ b/ext/bcmath/libbcmath/src/raise.c @@ -32,19 +32,15 @@ #include "bcmath.h" #include #include -#include /* Raise NUM1 to the NUM2 power. The result is placed in RESULT. Maximum exponent is LONG_MAX. If a NUM2 is not an integer, only the integer part is used. */ -void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) -{ +void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) { bc_num temp, power; - size_t rscale; - size_t pwrscale; - size_t calcscale; + size_t rscale, pwrscale, calcscale; bool is_neg; /* Special case if exponent is a zero. */ @@ -69,7 +65,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) pwrscale = num1->n_scale; while ((exponent & 1) == 0) { pwrscale = 2 * pwrscale; - bc_multiply(power, power, &power, pwrscale); + bc_square(power, &power, pwrscale); exponent = exponent >> 1; } temp = bc_copy_num(power); @@ -79,7 +75,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) /* Do the calculation. */ while (exponent > 0) { pwrscale = 2 * pwrscale; - bc_multiply(power, power, &power, pwrscale); + bc_square(power, &power, pwrscale); if ((exponent & 1) == 1) { calcscale = pwrscale + calcscale; bc_multiply(temp, power, &temp, calcscale); @@ -102,8 +98,7 @@ void bc_raise(bc_num num1, long exponent, bc_num *result, size_t scale) } /* This is used internally by BCMath */ -void bc_raise_bc_exponent(bc_num base, bc_num expo, bc_num *result, size_t scale) -{ +void bc_raise_bc_exponent(bc_num base, bc_num expo, bc_num *result, size_t scale) { /* Exponent must not have fractional part */ assert(expo->n_scale == 0); diff --git a/ext/bcmath/libbcmath/src/raisemod.c b/ext/bcmath/libbcmath/src/raisemod.c index 93bd4193da45c..ecd863704fc07 100644 --- a/ext/bcmath/libbcmath/src/raisemod.c +++ b/ext/bcmath/libbcmath/src/raisemod.c @@ -30,7 +30,6 @@ *************************************************************************/ #include "bcmath.h" -#include /* Raise BASE to the EXPO power, reduced modulo MOD. The result is placed in RESULT. */ raise_mod_status bc_raisemod(bc_num base, bc_num expo, bc_num mod, bc_num *result, size_t scale) diff --git a/ext/bcmath/libbcmath/src/recmul.c b/ext/bcmath/libbcmath/src/recmul.c index ec89c461fa188..5d8a5a4c15cda 100644 --- a/ext/bcmath/libbcmath/src/recmul.c +++ b/ext/bcmath/libbcmath/src/recmul.c @@ -66,7 +66,6 @@ static void _bc_simp_mul(bc_num n1, size_t n1len, bc_num n2, int n2len, bc_num * char *n1ptr, *n2ptr, *pvptr; char *n1end, *n2end; /* To the end of n1 and n2. */ int sum = 0; - int prodlen = n1len + n2len + 1; *prod = bc_new_num (prodlen, 0); @@ -229,7 +228,6 @@ static void _bc_rec_mul(bc_num u, size_t ulen, bc_num v, size_t vlen, bc_num *pr _bc_rec_mul(u0, u0->n_len, v0, v0->n_len, &m3); } - /* Initialize product */ *prod = bc_new_num(ulen + vlen + 1, 0); if (!m1zero) { @@ -241,17 +239,69 @@ static void _bc_rec_mul(bc_num u, size_t ulen, bc_num v, size_t vlen, bc_num *pr _bc_shift_addsub(*prod, m2, n, d1->n_sign != d2->n_sign); /* Now clean up! */ - bc_free_num (&u1); bc_free_num (&u0); + bc_free_num (&u1); + bc_free_num (&v0); bc_free_num (&v1); bc_free_num (&m1); - bc_free_num (&v0); bc_free_num (&m2); bc_free_num (&m3); bc_free_num (&d1); bc_free_num (&d2); } +/** + * Representation of _bc_rec_mul for squaring + */ +static void _bc_rec_square(bc_num u, size_t ulen, bc_num *prod) { + bc_num u0, u1; + bc_num m2, m3, d1; + size_t n; + + /* Base case? */ + if (ulen < MUL_SMALL_DIGITS) { + _bc_simp_mul(u, ulen, u, ulen, prod); + return; + } + + /* Calculate n */ + n = (ulen + 1) / 2; + + if (ulen == 0) { + return; + } else { + u1 = new_sub_num(ulen - n, 0, u->n_value); + u0 = new_sub_num(n, 0, u->n_value + ulen - n); + _bc_rec_square(u0, u0->n_len, &m3); + _bc_rm_leading_zeros(u1); + _bc_rm_leading_zeros(u0); + bc_init_num(&d1); + bc_sub(u1, u0, &d1, 0); + _bc_rec_square(d1, d1->n_len, &m2); + } + + /* Do recursive multiplies and shifted adds. */ + *prod = bc_new_num(ulen + ulen + 1, 0); + + if (!bc_is_zero(u1)) { + bc_num m1; + _bc_rec_square(u1, u1->n_len, &m1); + _bc_shift_addsub(*prod, m1, 2 * n, false); + _bc_shift_addsub(*prod, m1, n, false); + bc_free_num (&m1); + } + _bc_shift_addsub(*prod, m3, n, false); + _bc_shift_addsub(*prod, m3, 0, false); + _bc_shift_addsub(*prod, m2, n, true); + + /* Now clean up! */ + bc_free_num (&u0); + bc_free_num (&u1); + bc_free_num (&m2); + bc_free_num (&m3); + bc_free_num (&d1); +} + /* The multiply routine. N2 times N1 is put int PROD with the scale of the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)). */ @@ -283,3 +333,22 @@ void bc_multiply(bc_num n1, bc_num n2, bc_num *prod, size_t scale) bc_free_num(prod); *prod = pval; } + +void bc_square(bc_num num, bc_num *prod, size_t scale) { + bc_num pval; + + /* Do the square */ + _bc_rec_square(num, num->n_len + num->n_scale, &pval); + + /* Assign to prod and clean up the number. */ + pval->n_sign = PLUS; + pval->n_value = pval->n_ptr; + pval->n_len = num->n_len * 2 + 1; + pval->n_scale = MIN(num->n_scale * 2, MAX(scale, num->n_scale)); + _bc_rm_leading_zeros(pval); + if (bc_is_zero(pval)) { + pval->n_sign = PLUS; + } + bc_free_num(prod); + *prod = pval; +} diff --git a/ext/bcmath/libbcmath/src/sub.c b/ext/bcmath/libbcmath/src/sub.c index d90f1a8858f29..6937d3a8e47b8 100644 --- a/ext/bcmath/libbcmath/src/sub.c +++ b/ext/bcmath/libbcmath/src/sub.c @@ -39,8 +39,7 @@ N2 is subtracted from N1 and the result placed in RESULT. SCALE_MIN is the minimum scale for the result. */ -void bc_sub(bc_num n1, bc_num n2, bc_num *result, size_t scale_min) -{ +void bc_sub(bc_num n1, bc_num n2, bc_num *result, size_t scale_min) { bc_num diff = NULL; if (n1->n_sign != n2->n_sign) { diff --git a/ext/bcmath/tests/bcpow_zero.phpt b/ext/bcmath/tests/bcpow_zero.phpt index f87a8db6a96ef..6e1e7eb34682a 100644 --- a/ext/bcmath/tests/bcpow_zero.phpt +++ b/ext/bcmath/tests/bcpow_zero.phpt @@ -18,7 +18,9 @@ $baseNumbers = [ "0", ]; -run_bcmath_tests($baseNumbers, $exponents, "**", bcpow(...)); +//run_bcmath_tests($baseNumbers, $exponents, "**", bcpow(...)); + +echo bcpow("0.0000001",1515151551); ?> --EXPECT--