Skip to content

Commit 5359392

Browse files
[RFC] Add bcfloor, bcceil and bcround to BCMath (#13096)
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 <girgias@php.net>
1 parent 78ec2bd commit 5359392

27 files changed

+1500
-38
lines changed

Zend/Optimizer/zend_func_infos.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ static const func_info_t func_infos[] = {
2727
F1("bcpowmod", MAY_BE_STRING),
2828
F1("bcpow", MAY_BE_STRING),
2929
F1("bcsqrt", MAY_BE_STRING),
30+
F1("bcfloor", MAY_BE_STRING),
31+
F1("bcceil", MAY_BE_STRING),
32+
F1("bcround", MAY_BE_STRING),
3033
FN("bzopen", MAY_BE_RESOURCE|MAY_BE_FALSE),
3134
F1("bzerror", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING),
3235
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),

ext/bcmath/bcmath.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,96 @@ PHP_FUNCTION(bccomp)
643643
}
644644
/* }}} */
645645

646+
/* {{{ floor or ceil */
647+
static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)
648+
{
649+
zend_string *numstr;
650+
bc_num num, result;
651+
652+
ZEND_PARSE_PARAMETERS_START(1, 1)
653+
Z_PARAM_STR(numstr)
654+
ZEND_PARSE_PARAMETERS_END();
655+
656+
bc_init_num(&num);
657+
bc_init_num(&result);
658+
659+
if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
660+
zend_argument_value_error(1, "is not well-formed");
661+
goto cleanup;
662+
}
663+
664+
bc_floor_or_ceil(num, is_floor, &result);
665+
RETVAL_STR(bc_num2str_ex(result, 0));
666+
667+
cleanup: {
668+
bc_free_num(&num);
669+
bc_free_num(&result);
670+
};
671+
}
672+
/* }}} */
673+
674+
/* {{{ Returns floor of num */
675+
PHP_FUNCTION(bcfloor)
676+
{
677+
bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
678+
}
679+
/* }}} */
680+
681+
/* {{{ Returns ceil of num */
682+
PHP_FUNCTION(bcceil)
683+
{
684+
bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
685+
}
686+
/* }}} */
687+
688+
/* {{{ Returns num rounded to the digits specified by precision. */
689+
PHP_FUNCTION(bcround)
690+
{
691+
zend_string *numstr;
692+
zend_long precision = 0;
693+
zend_long mode = PHP_ROUND_HALF_UP;
694+
bc_num num, result;
695+
696+
ZEND_PARSE_PARAMETERS_START(1, 3)
697+
Z_PARAM_STR(numstr)
698+
Z_PARAM_OPTIONAL
699+
Z_PARAM_LONG(precision)
700+
Z_PARAM_LONG(mode)
701+
ZEND_PARSE_PARAMETERS_END();
702+
703+
switch (mode) {
704+
case PHP_ROUND_HALF_UP:
705+
case PHP_ROUND_HALF_DOWN:
706+
case PHP_ROUND_HALF_EVEN:
707+
case PHP_ROUND_HALF_ODD:
708+
case PHP_ROUND_CEILING:
709+
case PHP_ROUND_FLOOR:
710+
case PHP_ROUND_TOWARD_ZERO:
711+
case PHP_ROUND_AWAY_FROM_ZERO:
712+
break;
713+
default:
714+
zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
715+
return;
716+
}
717+
718+
bc_init_num(&num);
719+
bc_init_num(&result);
720+
721+
if (php_str2num(&num, ZSTR_VAL(numstr)) == FAILURE) {
722+
zend_argument_value_error(1, "is not well-formed");
723+
goto cleanup;
724+
}
725+
726+
bc_round(num, precision, mode, &result);
727+
RETVAL_STR(bc_num2str_ex(result, result->n_scale));
728+
729+
cleanup: {
730+
bc_free_num(&num);
731+
bc_free_num(&result);
732+
};
733+
}
734+
/* }}} */
735+
646736
/* {{{ Sets default scale parameter for all bc math functions */
647737
PHP_FUNCTION(bcscale)
648738
{

ext/bcmath/bcmath.stub.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,12 @@ function bcsqrt(string $num, ?int $scale = null): string {}
2929
function bccomp(string $num1, string $num2, ?int $scale = null): int {}
3030

3131
function bcscale(?int $scale = null): int {}
32+
33+
/** @refcount 1 */
34+
function bcfloor(string $num): string {}
35+
36+
/** @refcount 1 */
37+
function bcceil(string $num): string {}
38+
39+
/** @refcount 1 */
40+
function bcround(string $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): string {}

ext/bcmath/bcmath_arginfo.h

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/bcmath/config.m4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ if test "$PHP_BCMATH" != "no"; then
77
PHP_NEW_EXTENSION(bcmath, bcmath.c \
88
libbcmath/src/add.c libbcmath/src/div.c libbcmath/src/init.c libbcmath/src/neg.c libbcmath/src/raisemod.c libbcmath/src/sub.c \
99
libbcmath/src/compare.c libbcmath/src/divmod.c libbcmath/src/int2num.c libbcmath/src/num2long.c libbcmath/src/output.c libbcmath/src/recmul.c \
10-
libbcmath/src/sqrt.c libbcmath/src/zero.c libbcmath/src/doaddsub.c libbcmath/src/nearzero.c libbcmath/src/num2str.c libbcmath/src/raise.c \
11-
libbcmath/src/rmzero.c libbcmath/src/str2num.c,
10+
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 \
11+
libbcmath/src/raise.c libbcmath/src/rmzero.c libbcmath/src/round.c libbcmath/src/str2num.c,
1212
$ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
1313
PHP_ADD_BUILD_DIR($ext_builddir/libbcmath/src)
1414
AC_DEFINE(HAVE_BCMATH, 1, [Whether you have bcmath])

ext/bcmath/config.w32

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ if (PHP_BCMATH == "yes") {
77
ADD_SOURCES("ext/bcmath/libbcmath/src", "add.c div.c init.c neg.c \
88
raisemod.c sub.c compare.c divmod.c int2num.c \
99
num2long.c output.c recmul.c sqrt.c zero.c doaddsub.c \
10-
nearzero.c num2str.c raise.c rmzero.c str2num.c", "bcmath");
10+
floor_or_ceil.c nearzero.c num2str.c raise.c rmzero.c str2num.c \
11+
round.c", "bcmath");
1112

1213
AC_DEFINE('HAVE_BCMATH', 1, 'Have BCMATH library');
1314
}

ext/bcmath/libbcmath/src/bcmath.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ typedef struct bc_struct {
5858
#include "zend.h"
5959
#include <stdbool.h>
6060
#include "zend_string.h"
61-
#include "../../php_bcmath.h" /* Needed for BCG() macro */
61+
62+
/* Needed for BCG() macro and PHP_ROUND_XXX */
63+
#include "../../php_bcmath.h"
6264

6365
/* The base used in storing the numbers in n_value above.
6466
Currently, this MUST be 10. */
@@ -125,6 +127,10 @@ bool bc_modulo(bc_num num1, bc_num num2, bc_num *resul, size_t scale);
125127

126128
bool bc_divmod(bc_num num1, bc_num num2, bc_num *quo, bc_num *rem, size_t scale);
127129

130+
void bc_floor_or_ceil(bc_num num, bool is_floor, bc_num *result);
131+
132+
void bc_round(bc_num num, zend_long places, zend_long mode, bc_num *result);
133+
128134
typedef enum {
129135
OK,
130136
BASE_HAS_FRACTIONAL,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Saki Takamachi <saki@php.net> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "bcmath.h"
18+
#include "private.h"
19+
#include <stddef.h>
20+
21+
void bc_floor_or_ceil(bc_num num, bool is_floor, bc_num *result)
22+
{
23+
/* clear result */
24+
bc_free_num(result);
25+
26+
/* Initialize result */
27+
*result = bc_new_num(num->n_len, 0);
28+
(*result)->n_sign = num->n_sign;
29+
30+
/* copy integer part */
31+
memcpy((*result)->n_value, num->n_value, num->n_len);
32+
33+
/* If the number is positive and we are flooring, then nothing else needs to be done.
34+
* Similarly, if the number is negative and we are ceiling, then nothing else needs to be done. */
35+
if (num->n_scale == 0 || (*result)->n_sign == (is_floor ? PLUS : MINUS)) {
36+
return;
37+
}
38+
39+
/* check fractional part. */
40+
size_t count = num->n_scale;
41+
const char *nptr = num->n_value + num->n_len;
42+
while ((count > 0) && (*nptr == 0)) {
43+
count--;
44+
nptr++;
45+
}
46+
47+
/* If all digits past the decimal point are 0 */
48+
if (count == 0) {
49+
return;
50+
}
51+
52+
/* Increment the absolute value of the result by 1 and add sign information */
53+
bc_num tmp = _bc_do_add(*result, BCG(_one_), 0);
54+
tmp->n_sign = (*result)->n_sign;
55+
bc_free_num(result);
56+
*result = tmp;
57+
}

0 commit comments

Comments
 (0)