From 4c3dafcfef1ce441704f68b1e45786738d8a6616 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 11 Mar 2025 09:15:49 +0900 Subject: [PATCH 1/3] added bc_convert_vector_to_char --- ext/bcmath/libbcmath/src/convert.h | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ext/bcmath/libbcmath/src/convert.h b/ext/bcmath/libbcmath/src/convert.h index e278ae5fef1aa..474275e07f9dc 100644 --- a/ext/bcmath/libbcmath/src/convert.h +++ b/ext/bcmath/libbcmath/src/convert.h @@ -85,4 +85,47 @@ static inline void bc_convert_to_vector_with_zero_pad(BC_VECTOR *n_vector, const bc_convert_to_vector(n_vector, nend, nlen); } +static inline void bc_convert_vector_to_char(char *nptr, char *nend, BC_VECTOR *vectors, size_t vsize) +{ + size_t i; + for (i = 0; i < vsize - 1; i++) { +#if BC_VECTOR_SIZE == 4 + bc_write_bcd_representation(vectors[i], nend - 3); + nend -= 4; +#else + bc_write_bcd_representation(vectors[i] / 10000, nend - 7); + bc_write_bcd_representation(vectors[i] % 10000, nend - 3); + nend -= 8; +#endif + } + + while (nend >= nptr) { + *nend-- = vectors[i] % BASE; + vectors[i] /= BASE; + } +} + +static inline void bc_convert_vector_to_char_with_skip(char *nptr, char *nend, BC_VECTOR *vectors, size_t vsize, size_t skip) +{ + size_t vskip = skip / BC_VECTOR_SIZE; + vsize -= vskip; + vectors += vskip; + + size_t protruded_skip = skip % BC_VECTOR_SIZE; + if (protruded_skip > 0) { + BC_VECTOR tmp = *vectors; + tmp /= BC_POW_10_LUT[protruded_skip]; + size_t write_size = MIN(nend - nptr + 1, BC_VECTOR_SIZE - protruded_skip); + for (size_t i = 0; i < write_size; i++) { + *nend-- = tmp % BASE; + tmp /= BASE; + } + vectors++; + vsize--; + } + if (vsize > 0) { + bc_convert_vector_to_char(nptr, nend, vectors, vsize); + } +} + #endif From 638cf4ca6a73df642153661458c9c2a8f49138a6 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 11 Mar 2025 09:16:44 +0900 Subject: [PATCH 2/3] Optimized to get the remainder at the same time as division --- ext/bcmath/config.m4 | 1 - ext/bcmath/config.w32 | 2 +- ext/bcmath/libbcmath/src/bcmath.h | 11 +- ext/bcmath/libbcmath/src/div.c | 225 ++++++++++++++++++++++-------- ext/bcmath/libbcmath/src/divmod.c | 89 ------------ 5 files changed, 176 insertions(+), 152 deletions(-) delete mode 100644 ext/bcmath/libbcmath/src/divmod.c diff --git a/ext/bcmath/config.m4 b/ext/bcmath/config.m4 index a29f2af75ca8e..82e61be952f95 100644 --- a/ext/bcmath/config.m4 +++ b/ext/bcmath/config.m4 @@ -10,7 +10,6 @@ if test "$PHP_BCMATH" != "no"; then libbcmath/src/compare.c libbcmath/src/convert.c libbcmath/src/div.c - libbcmath/src/divmod.c libbcmath/src/doaddsub.c libbcmath/src/floor_or_ceil.c libbcmath/src/long2num.c diff --git a/ext/bcmath/config.w32 b/ext/bcmath/config.w32 index 74d1b38802eea..c45026fd11f82 100644 --- a/ext/bcmath/config.w32 +++ b/ext/bcmath/config.w32 @@ -5,7 +5,7 @@ ARG_ENABLE("bcmath", "bc style precision math functions", "yes"); if (PHP_BCMATH == "yes") { EXTENSION("bcmath", "bcmath.c", null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); ADD_SOURCES("ext/bcmath/libbcmath/src", "add.c div.c init.c neg.c \ - raisemod.c sub.c compare.c divmod.c int2num.c long2num.c \ + raisemod.c sub.c compare.c int2num.c long2num.c \ num2long.c recmul.c sqrt.c zero.c doaddsub.c \ floor_or_ceil.c nearzero.c num2str.c raise.c rmzero.c str2num.c \ round.c convert.c", "bcmath"); diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h index f7e14019bd336..e17e746d96874 100644 --- a/ext/bcmath/libbcmath/src/bcmath.h +++ b/ext/bcmath/libbcmath/src/bcmath.h @@ -149,11 +149,7 @@ bc_num bc_multiply(bc_num n1, bc_num n2, size_t scale); bc_num bc_square(bc_num n1, size_t scale); -bool bc_divide(bc_num n1, bc_num n2, bc_num *quot, size_t scale); - -bool bc_modulo(bc_num num1, bc_num num2, bc_num *resul, size_t scale); - -bool bc_divmod(bc_num num1, bc_num num2, bc_num *quo, bc_num *rem, size_t scale); +bool bc_divide_ex(bc_num numerator, bc_num divisor, bc_num *quot, bc_num *rem, size_t scale, bool use_quot, bool use_rem); bc_num bc_floor_or_ceil(bc_num num, bool is_floor); @@ -182,4 +178,9 @@ bool bc_sqrt(bc_num *num, size_t scale); #define bc_free_num(num) _bc_free_num_ex((num), 0) #define bc_num2str(num) bc_num2str_ex((num), (num->n_scale)) +/* div and mod */ +#define bc_divide(n1, n2, quot, scale) bc_divide_ex((n1), (n2), (quot), NULL, (scale), true, false) +#define bc_modulo(n1, n2, rem, scale) bc_divide_ex((n1), (n2), NULL, (rem), (scale), false, true) +#define bc_divmod(n1, n2, quot, rem, scale) bc_divide_ex((n1), (n2), (quot), (rem), (scale), true, true) + #endif diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c index cda452569dedc..d3480973492b7 100644 --- a/ext/bcmath/libbcmath/src/div.c +++ b/ext/bcmath/libbcmath/src/div.c @@ -61,6 +61,7 @@ static inline void bc_fast_div( } /* last */ quot_vectors[0] = numerator_vectors[0] / divisor_vector; + numerator_vectors[0] -= divisor_vector * quot_vectors[0]; } /* @@ -248,12 +249,15 @@ static inline void bc_standard_div( div_carry = numerator_vectors[numerator_top_index - i]; numerator_vectors[numerator_top_index - i] = 0; } + numerator_vectors[numerator_top_index - quot_arr_size + 1] = div_carry; } static void bc_do_div( const char *numerator, size_t numerator_size, size_t numerator_readable_size, const char *divisor, size_t divisor_size, - bc_num *quot, size_t quot_size + bc_num *quot, size_t quot_size, + bc_num *rem, size_t rem_over_size, size_t rem_write_size, + bool use_quot, bool use_rem ) { size_t numerator_arr_size = (numerator_size + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE; size_t divisor_arr_size = (divisor_size + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE; @@ -282,70 +286,121 @@ static void bc_do_div( } /* Convert to bc_num */ - char *qptr = (*quot)->n_value; - char *qend = qptr + (*quot)->n_len + (*quot)->n_scale - 1; - - size_t i; - for (i = 0; i < quot_real_arr_size - 1; i++) { -#if BC_VECTOR_SIZE == 4 - bc_write_bcd_representation(quot_vectors[i], qend - 3); - qend -= 4; -#else - bc_write_bcd_representation(quot_vectors[i] / 10000, qend - 7); - bc_write_bcd_representation(quot_vectors[i] % 10000, qend - 3); - qend -= 8; -#endif + if (use_quot) { + char *qptr = (*quot)->n_value; + char *qend = qptr + (*quot)->n_len + (*quot)->n_scale - 1; + bc_convert_vector_to_char(qptr, qend, quot_vectors, quot_real_arr_size); } - while (qend >= qptr) { - *qend-- = quot_vectors[i] % BASE; - quot_vectors[i] /= BASE; + if (use_rem) { + size_t rem_arr_size = (rem_write_size + rem_over_size + BC_VECTOR_SIZE - 1) / BC_VECTOR_SIZE; + BC_VECTOR *rem_vectors = numerator_vectors; + + char *rptr = (*rem)->n_value; + char *rend = rptr + rem_write_size - 1; + if (rem_over_size > 0) { + bc_convert_vector_to_char_with_skip(rptr, rend, rem_vectors, rem_arr_size, rem_over_size); + } else { + bc_convert_vector_to_char(rptr, rend, rem_vectors, rem_arr_size); + } } efree(numerator_vectors); } -static inline void bc_divide_by_one(bc_num numerator, bc_num *quot, size_t quot_scale) +static inline void bc_divide_copy_numerator(bc_num numerator, bc_num *num, size_t scale) { - quot_scale = MIN(numerator->n_scale, quot_scale); - *quot = bc_new_num_nonzeroed(numerator->n_len, quot_scale); - char *qptr = (*quot)->n_value; - memcpy(qptr, numerator->n_value, numerator->n_len + quot_scale); + scale = MIN(numerator->n_scale, scale); + *num = bc_new_num_nonzeroed(numerator->n_len, scale); + char *nptr = (*num)->n_value; + memcpy(nptr, numerator->n_value, numerator->n_len + scale); } -static inline void bc_divide_by_pow_10( - const char *numeratorptr, size_t numerator_readable_size, bc_num *quot, size_t quot_size, size_t quot_scale) +static inline void bc_divide_by_one( + bc_num numerator, bc_num divisor, bc_num *quot, size_t quot_scale, bc_num *rem, size_t rem_scale, + bool use_quot, bool use_rem) { - char *qptr = (*quot)->n_value; - for (size_t i = quot_size; i <= quot_scale; i++) { - *qptr++ = 0; + if (use_quot) { + bc_divide_copy_numerator(numerator, quot, quot_scale); + (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; } + if (use_rem) { + *rem = bc_new_num_nonzeroed(1, MIN(numerator->n_scale, rem_scale)); /* 1 is for 0 */ + (*rem)->n_value[0] = 0; + memcpy((*rem)->n_value + 1, numerator->n_value + numerator->n_len, (*rem)->n_scale); + if (bc_is_zero(*rem)) { + (*rem)->n_sign = PLUS; + (*rem)->n_scale = 0; + } else { + (*rem)->n_sign = numerator->n_sign; + } + } +} - size_t numerator_use_size = quot_size > numerator_readable_size ? numerator_readable_size : quot_size; - memcpy(qptr, numeratorptr, numerator_use_size); - qptr += numerator_use_size; - - if (numerator_use_size < (*quot)->n_len) { - /* e.g. 12.3 / 0.01 <=> 1230 */ - for (size_t i = numerator_use_size; i < (*quot)->n_len; i++) { +static inline void bc_divide_by_pow_10( + const char *numeratorptr, size_t numerator_len, size_t numerator_readable_size, size_t numerator_leading_zeros, + bc_num *quot, size_t quot_size, size_t quot_scale, bc_num *rem, + bool use_quot, bool use_rem) +{ + if (use_quot) { + char *qptr = (*quot)->n_value; + for (size_t i = quot_size; i <= quot_scale; i++) { *qptr++ = 0; } - (*quot)->n_scale = 0; - } else { - char *qend = (*quot)->n_value + (*quot)->n_len + (*quot)->n_scale; - (*quot)->n_scale -= qend - qptr; + + size_t numerator_use_size = quot_size > numerator_readable_size ? numerator_readable_size : quot_size; + memcpy(qptr, numeratorptr, numerator_use_size); + qptr += numerator_use_size; + + if (numerator_use_size < (*quot)->n_len) { + /* e.g. 12.3 / 0.01 <=> 1230 */ + for (size_t i = numerator_use_size; i < (*quot)->n_len; i++) { + *qptr++ = 0; + } + (*quot)->n_scale = 0; + } else { + char *qend = (*quot)->n_value + (*quot)->n_len + (*quot)->n_scale; + (*quot)->n_scale -= qend - qptr; + } + } + + if (use_rem) { + char *rptr = (*rem)->n_value; + size_t rem_leading_zeros = numerator_leading_zeros + quot_size - (numerator_len - (*rem)->n_len); + if ((*rem)->n_len + (*rem)->n_scale <= rem_leading_zeros) { + bc_free_num(rem); + *rem = bc_copy_num(BCG(_zero_)); + return; + } + for (size_t i = 0; i < rem_leading_zeros; i++) { + *rptr++ = 0; + } + _bc_rm_leading_zeros(*rem); + if (bc_is_zero(*rem)) { + (*rem)->n_sign = PLUS; + (*rem)->n_scale = 0; + } } } -bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) +bool bc_divide_ex(bc_num numerator, bc_num divisor, bc_num *quot, bc_num *rem, size_t scale, bool use_quot, bool use_rem) { /* divide by zero */ if (bc_is_zero(divisor)) { return false; } - bc_free_num(quot); - size_t quot_scale = scale; + size_t quot_scale = 0; + size_t rem_scale = 0; + if (use_quot) { + bc_free_num(quot); + } + if (use_rem) { + bc_free_num(rem); + rem_scale = scale; + } else { + quot_scale = scale; + } /* If numerator is zero, the quotient is always zero. */ if (bc_is_zero(numerator)) { @@ -354,8 +409,7 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) /* If divisor is 1 / -1, the quotient's n_value is equal to numerator's n_value. */ if (_bc_do_compare(divisor, BCG(_one_), divisor->n_scale, false) == BCMATH_EQUAL) { - bc_divide_by_one(numerator, quot, quot_scale); - (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + bc_divide_by_one(numerator, divisor, quot, quot_scale, rem, rem_scale, use_quot, use_rem); return true; } @@ -404,10 +458,50 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) } size_t quot_size = numerator_size - divisor_size + 1; /* numerator_size >= divisor_size */ - if (quot_size > quot_scale) { - *quot = bc_new_num_nonzeroed(quot_size - quot_scale, quot_scale); - } else { - *quot = bc_new_num_nonzeroed(1, quot_scale); /* 1 is for 0 */ + if (use_quot) { + if (quot_size > quot_scale) { + *quot = bc_new_num_nonzeroed(quot_size - quot_scale, quot_scale); + } else { + *quot = bc_new_num_nonzeroed(1, quot_scale); /* 1 is for 0 */ + } + (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + } + + /** + * If the calculation uses more digits than the scale of rem, writing the vector directly to rem + * will exceed the size, so calculate the excess size in advance. + */ + size_t rem_over_size = 0; + + /** + * Conversely, there are cases where the vector does not fill the rem size. + * In this case, the size to be written is calculated in advance to determine the start position for writing to rem. + */ + size_t rem_write_size = 0; + if (use_rem) { + size_t divisor_frac_size = divisor->n_scale > divisor_trailing_zeros ? divisor->n_scale - divisor_trailing_zeros : 0; + rem_scale = MIN(MAX(numerator->n_scale, divisor_frac_size), rem_scale); + *rem = bc_new_num_nonzeroed(divisor->n_len, rem_scale); + (*rem)->n_sign = numerator->n_sign; + + if (divisor_frac_size > rem_scale) { + rem_over_size = divisor_frac_size - rem_scale; + rem_write_size = (*rem)->n_len + rem_scale; + } else { + if (divisor_frac_size > 0) { + rem_write_size = (*rem)->n_len + divisor_frac_size; + } else { + /* e.g. 100 % 30 */ + rem_write_size = (*rem)->n_len - (divisor_trailing_zeros - divisor->n_scale); + } + } + + size_t rem_size = (*rem)->n_len + (*rem)->n_scale; + if (rem_size > rem_write_size) { + size_t copy_size = rem_size - rem_write_size; + size_t len_diff = numerator->n_len - (*rem)->n_len; + memcpy((*rem)->n_value + rem_write_size, numerator->n_value + rem_write_size + len_diff, copy_size); + } } /* Size that can be read from numeratorptr */ @@ -415,8 +509,11 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) /* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */ if (divisor_size == 1 && *divisorptr == 1) { - bc_divide_by_pow_10(numeratorptr, numerator_readable_size, quot, quot_size, quot_scale); - (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + bc_divide_by_pow_10( + numeratorptr, numerator->n_len, numerator_readable_size, numerator_leading_zeros, + quot, quot_size, quot_scale, rem, + use_quot, use_rem + ); return true; } @@ -424,18 +521,34 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) bc_do_div( numeratorptr, numerator_size, numerator_readable_size, divisorptr, divisor_size, - quot, quot_size + quot, quot_size, + rem, rem_over_size, rem_write_size, + use_quot, use_rem ); - _bc_rm_leading_zeros(*quot); - if (bc_is_zero(*quot)) { - (*quot)->n_sign = PLUS; - } else { - (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + if (use_quot) { + _bc_rm_leading_zeros(*quot); + if (bc_is_zero(*quot)) { + (*quot)->n_sign = PLUS; + (*quot)->n_scale = 0; + } + } + if (use_rem) { + _bc_rm_leading_zeros(*rem); + if (bc_is_zero(*rem)) { + (*rem)->n_sign = PLUS; + (*rem)->n_scale = 0; + } } return true; quot_zero: - *quot = bc_copy_num(BCG(_zero_)); + if (use_quot) { + *quot = bc_copy_num(BCG(_zero_)); + } + if (use_rem) { + bc_divide_copy_numerator(numerator, rem, rem_scale); + (*rem)->n_sign = numerator->n_sign; + } return true; } diff --git a/ext/bcmath/libbcmath/src/divmod.c b/ext/bcmath/libbcmath/src/divmod.c deleted file mode 100644 index 294a281b2e687..0000000000000 --- a/ext/bcmath/libbcmath/src/divmod.c +++ /dev/null @@ -1,89 +0,0 @@ -/* divmod.c: bcmath library file. */ -/* - Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc. - Copyright (C) 2000 Philip A. Nelson - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. (LICENSE) - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to: - - The Free Software Foundation, Inc. - 59 Temple Place, Suite 330 - Boston, MA 02111-1307 USA. - - You may contact the author by: - e-mail: philnelson@acm.org - us-mail: Philip A. Nelson - Computer Science Department, 9062 - Western Washington University - Bellingham, WA 98226-9062 - -*************************************************************************/ - -#include "bcmath.h" -#include -#include - - -/* Division *and* modulo for numbers. This computes both NUM1 / NUM2 and - NUM1 % NUM2 and puts the results in QUOT and REM, except that if QUOT - is NULL then that store will be omitted. - false is returned if divisor is 0. - true otherwise for success. - */ - -bool bc_divmod(bc_num num1, bc_num num2, bc_num *quot, bc_num *rem, size_t scale) -{ - bc_num quotient = NULL; - bc_num temp; - size_t rscale; - - /* Cannot divide/mod by 0. */ - if (bc_is_zero(num2)) { - return false; - } - - /* Calculate final scale. */ - rscale = MAX (num1->n_scale, num2->n_scale + scale); - bc_init_num(&temp); - - /* Calculate it. */ - bc_divide(num1, num2, &temp, 0); - if (quot) { - quotient = bc_copy_num(temp); - } - bc_multiply_ex(temp, num2, &temp, rscale); - bc_sub_ex(num1, temp, rem, rscale); - bc_free_num (&temp); - - if (quot) { - bc_free_num (quot); - *quot = quotient; - } - - /* The value of rscale changes during processing. Here we use the value of scale. It's not a typo. */ - (*rem)->n_scale = MIN(scale, (*rem)->n_scale); - if (bc_is_zero(*rem)) { - (*rem)->n_sign = PLUS; - } - - return true; -} - - -/* Modulo for numbers. This computes NUM1 % NUM2 and puts the - result in RESULT. */ - -bool bc_modulo(bc_num num1, bc_num num2, bc_num *result, size_t scale) -{ - return bc_divmod(num1, num2, NULL, result, scale); -} From b2403a0ab61071c4094e0ff6a217a6d38bfb9534 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 11 Mar 2025 10:41:24 +0900 Subject: [PATCH 3/3] fixed powmod --- ext/bcmath/libbcmath/src/raisemod.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ext/bcmath/libbcmath/src/raisemod.c b/ext/bcmath/libbcmath/src/raisemod.c index 696fbe882f00b..d5d467d519a29 100644 --- a/ext/bcmath/libbcmath/src/raisemod.c +++ b/ext/bcmath/libbcmath/src/raisemod.c @@ -74,13 +74,23 @@ raise_mod_status bc_raisemod(bc_num base, bc_num expo, bc_num mod, bc_num *resul /* Do the calculation. */ while (!bc_is_zero(exponent)) { - (void) bc_divmod(exponent, BCG(_two_), &exponent, &parity, 0); + bc_num temp_quot = bc_copy_num(exponent); + (void) bc_divmod(exponent, BCG(_two_), &temp_quot, &parity, 0); + bc_free_num(&exponent); + exponent = temp_quot; + if (!bc_is_zero(parity)) { bc_multiply_ex(temp, power, &temp, scale); - (void) bc_modulo(temp, modulus, &temp, scale); + bc_num temp_rem = bc_copy_num(temp); + (void) bc_modulo(temp, modulus, &temp_rem, scale); + bc_free_num(&temp); + temp = temp_rem; } bc_multiply_ex(power, power, &power, scale); - (void) bc_modulo(power, modulus, &power, scale); + bc_num temp_rem = bc_copy_num(power); + (void) bc_modulo(power, modulus, &temp_rem, scale); + bc_free_num(&power); + power = temp_rem; } /* Assign the value. */