Skip to content

Commit

Permalink
[RFC] Add bcfloor, bcceil and bcround to BCMath (php#13096)
Browse files Browse the repository at this point in the history
Implementation for the "Adding bcround, bcfloor and bcceil to BCMath" RFC: https://wiki.php.net/rfc/adding_bcround_bcfloor_bcceil_to_bcmath

* Separated round mode into separate header file

Co-authored-by: Gina Peter Banyard <[email protected]>
  • Loading branch information
SakiTakamachi and Girgias authored Apr 30, 2024
1 parent 78ec2bd commit 5359392
Show file tree
Hide file tree
Showing 27 changed files with 1,500 additions and 38 deletions.
3 changes: 3 additions & 0 deletions Zend/Optimizer/zend_func_infos.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static const func_info_t func_infos[] = {
F1("bcpowmod", MAY_BE_STRING),
F1("bcpow", MAY_BE_STRING),
F1("bcsqrt", MAY_BE_STRING),
F1("bcfloor", MAY_BE_STRING),
F1("bcceil", MAY_BE_STRING),
F1("bcround", MAY_BE_STRING),
FN("bzopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
F1("bzerror", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING),
F1("cal_from_jd", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_NULL),
Expand Down
90 changes: 90 additions & 0 deletions ext/bcmath/bcmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,96 @@ PHP_FUNCTION(bccomp)
}
/* }}} */

/* {{{ floor or ceil */
static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)
{
zend_string *numstr;
bc_num num, result;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(numstr)
ZEND_PARSE_PARAMETERS_END();

bc_init_num(&num);
bc_init_num(&result);

if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

bc_floor_or_ceil(num, is_floor, &result);
RETVAL_STR(bc_num2str_ex(result, 0));

cleanup: {
bc_free_num(&num);
bc_free_num(&result);
};
}
/* }}} */

/* {{{ Returns floor of num */
PHP_FUNCTION(bcfloor)
{
bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
}
/* }}} */

/* {{{ Returns ceil of num */
PHP_FUNCTION(bcceil)
{
bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
}
/* }}} */

/* {{{ Returns num rounded to the digits specified by precision. */
PHP_FUNCTION(bcround)
{
zend_string *numstr;
zend_long precision = 0;
zend_long mode = PHP_ROUND_HALF_UP;
bc_num num, result;

ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STR(numstr)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(precision)
Z_PARAM_LONG(mode)
ZEND_PARSE_PARAMETERS_END();

switch (mode) {
case PHP_ROUND_HALF_UP:
case PHP_ROUND_HALF_DOWN:
case PHP_ROUND_HALF_EVEN:
case PHP_ROUND_HALF_ODD:
case PHP_ROUND_CEILING:
case PHP_ROUND_FLOOR:
case PHP_ROUND_TOWARD_ZERO:
case PHP_ROUND_AWAY_FROM_ZERO:
break;
default:
zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
return;
}

bc_init_num(&num);
bc_init_num(&result);

if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
}

bc_round(num, precision, mode, &result);
RETVAL_STR(bc_num2str_ex(result, result->n_scale));

cleanup: {
bc_free_num(&num);
bc_free_num(&result);
};
}
/* }}} */

/* {{{ Sets default scale parameter for all bc math functions */
PHP_FUNCTION(bcscale)
{
Expand Down
9 changes: 9 additions & 0 deletions ext/bcmath/bcmath.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,12 @@ function bcsqrt(string $num, ?int $scale = null): string {}
function bccomp(string $num1, string $num2, ?int $scale = null): int {}

function bcscale(?int $scale = null): int {}

/** @refcount 1 */
function bcfloor(string $num): string {}

/** @refcount 1 */
function bcceil(string $num): string {}

/** @refcount 1 */
function bcround(string $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): string {}
20 changes: 19 additions & 1 deletion ext/bcmath/bcmath_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions ext/bcmath/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ if test "$PHP_BCMATH" != "no"; then
PHP_NEW_EXTENSION(bcmath, bcmath.c \
libbcmath/src/add.c libbcmath/src/div.c libbcmath/src/init.c libbcmath/src/neg.c libbcmath/src/raisemod.c libbcmath/src/sub.c \
libbcmath/src/compare.c libbcmath/src/divmod.c libbcmath/src/int2num.c libbcmath/src/num2long.c libbcmath/src/output.c libbcmath/src/recmul.c \
libbcmath/src/sqrt.c libbcmath/src/zero.c libbcmath/src/doaddsub.c libbcmath/src/nearzero.c libbcmath/src/num2str.c libbcmath/src/raise.c \
libbcmath/src/rmzero.c libbcmath/src/str2num.c,
libbcmath/src/sqrt.c libbcmath/src/zero.c libbcmath/src/doaddsub.c libbcmath/src/floor_or_ceil.c libbcmath/src/nearzero.c libbcmath/src/num2str.c \
libbcmath/src/raise.c libbcmath/src/rmzero.c libbcmath/src/round.c libbcmath/src/str2num.c,
$ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_ADD_BUILD_DIR($ext_builddir/libbcmath/src)
AC_DEFINE(HAVE_BCMATH, 1, [Whether you have bcmath])
Expand Down
3 changes: 2 additions & 1 deletion ext/bcmath/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ if (PHP_BCMATH == "yes") {
ADD_SOURCES("ext/bcmath/libbcmath/src", "add.c div.c init.c neg.c \
raisemod.c sub.c compare.c divmod.c int2num.c \
num2long.c output.c recmul.c sqrt.c zero.c doaddsub.c \
nearzero.c num2str.c raise.c rmzero.c str2num.c", "bcmath");
floor_or_ceil.c nearzero.c num2str.c raise.c rmzero.c str2num.c \
round.c", "bcmath");

AC_DEFINE('HAVE_BCMATH', 1, 'Have BCMATH library');
}
8 changes: 7 additions & 1 deletion ext/bcmath/libbcmath/src/bcmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ typedef struct bc_struct {
#include "zend.h"
#include <stdbool.h>
#include "zend_string.h"
#include "../../php_bcmath.h" /* Needed for BCG() macro */

/* Needed for BCG() macro and PHP_ROUND_XXX */
#include "../../php_bcmath.h"

/* The base used in storing the numbers in n_value above.
Currently, this MUST be 10. */
Expand Down Expand Up @@ -125,6 +127,10 @@ 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);

void bc_floor_or_ceil(bc_num num, bool is_floor, bc_num *result);

void bc_round(bc_num num, zend_long places, zend_long mode, bc_num *result);

typedef enum {
OK,
BASE_HAS_FRACTIONAL,
Expand Down
57 changes: 57 additions & 0 deletions ext/bcmath/libbcmath/src/floor_or_ceil.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Saki Takamachi <[email protected]> |
+----------------------------------------------------------------------+
*/

#include "bcmath.h"
#include "private.h"
#include <stddef.h>

void bc_floor_or_ceil(bc_num num, bool is_floor, bc_num *result)
{
/* clear result */
bc_free_num(result);

/* Initialize result */
*result = bc_new_num(num->n_len, 0);
(*result)->n_sign = num->n_sign;

/* copy integer part */
memcpy((*result)->n_value, num->n_value, num->n_len);

/* If the number is positive and we are flooring, then nothing else needs to be done.
* Similarly, if the number is negative and we are ceiling, then nothing else needs to be done. */
if (num->n_scale == 0 || (*result)->n_sign == (is_floor ? PLUS : MINUS)) {
return;
}

/* check fractional part. */
size_t count = num->n_scale;
const char *nptr = num->n_value + num->n_len;
while ((count > 0) && (*nptr == 0)) {
count--;
nptr++;
}

/* If all digits past the decimal point are 0 */
if (count == 0) {
return;
}

/* Increment the absolute value of the result by 1 and add sign information */
bc_num tmp = _bc_do_add(*result, BCG(_one_), 0);
tmp->n_sign = (*result)->n_sign;
bc_free_num(result);
*result = tmp;
}
Loading

0 comments on commit 5359392

Please sign in to comment.