diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 76628104528fe..538326baf4959 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -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), diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index f53032fabc35c..7be9d4b01528a 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -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) { diff --git a/ext/bcmath/bcmath.stub.php b/ext/bcmath/bcmath.stub.php index 7335b2c399be2..5f5e60d3804a8 100644 --- a/ext/bcmath/bcmath.stub.php +++ b/ext/bcmath/bcmath.stub.php @@ -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 {} diff --git a/ext/bcmath/bcmath_arginfo.h b/ext/bcmath/bcmath_arginfo.h index 858885f70f66a..91ab4fd156477 100644 --- a/ext/bcmath/bcmath_arginfo.h +++ b/ext/bcmath/bcmath_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f28dafc2a279f5421cd0d0e668fde0032e996ebc */ + * Stub hash: cd3d182e13cb0ca22b27c13a8d0a86c20fde5b76 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bcadd, 0, 2, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, num1, IS_STRING, 0) @@ -43,6 +43,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bcscale, 0, 0, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scale, IS_LONG, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bcfloor, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, num, IS_STRING, 0) +ZEND_END_ARG_INFO() + +#define arginfo_bcceil arginfo_bcfloor + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bcround, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, num, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, precision, IS_LONG, 0, "0") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "PHP_ROUND_HALF_UP") +ZEND_END_ARG_INFO() + ZEND_FUNCTION(bcadd); ZEND_FUNCTION(bcsub); ZEND_FUNCTION(bcmul); @@ -53,6 +65,9 @@ ZEND_FUNCTION(bcpow); ZEND_FUNCTION(bcsqrt); ZEND_FUNCTION(bccomp); ZEND_FUNCTION(bcscale); +ZEND_FUNCTION(bcfloor); +ZEND_FUNCTION(bcceil); +ZEND_FUNCTION(bcround); static const zend_function_entry ext_functions[] = { ZEND_FE(bcadd, arginfo_bcadd) @@ -65,5 +80,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(bcsqrt, arginfo_bcsqrt) ZEND_FE(bccomp, arginfo_bccomp) ZEND_FE(bcscale, arginfo_bcscale) + ZEND_FE(bcfloor, arginfo_bcfloor) + ZEND_FE(bcceil, arginfo_bcceil) + ZEND_FE(bcround, arginfo_bcround) ZEND_FE_END }; diff --git a/ext/bcmath/config.m4 b/ext/bcmath/config.m4 index ac654aba00770..6053b72ec1fda 100644 --- a/ext/bcmath/config.m4 +++ b/ext/bcmath/config.m4 @@ -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]) diff --git a/ext/bcmath/config.w32 b/ext/bcmath/config.w32 index 4f242daa8a5f8..a0c2aa51728ad 100644 --- a/ext/bcmath/config.w32 +++ b/ext/bcmath/config.w32 @@ -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'); } diff --git a/ext/bcmath/libbcmath/src/bcmath.h b/ext/bcmath/libbcmath/src/bcmath.h index de51ee7457110..27800d3425736 100644 --- a/ext/bcmath/libbcmath/src/bcmath.h +++ b/ext/bcmath/libbcmath/src/bcmath.h @@ -58,7 +58,9 @@ typedef struct bc_struct { #include "zend.h" #include #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. */ @@ -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, diff --git a/ext/bcmath/libbcmath/src/floor_or_ceil.c b/ext/bcmath/libbcmath/src/floor_or_ceil.c new file mode 100644 index 0000000000000..5c3b17c01b610 --- /dev/null +++ b/ext/bcmath/libbcmath/src/floor_or_ceil.c @@ -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 | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Saki Takamachi | + +----------------------------------------------------------------------+ +*/ + +#include "bcmath.h" +#include "private.h" +#include + +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; +} diff --git a/ext/bcmath/libbcmath/src/round.c b/ext/bcmath/libbcmath/src/round.c new file mode 100644 index 0000000000000..a96822abd530d --- /dev/null +++ b/ext/bcmath/libbcmath/src/round.c @@ -0,0 +1,179 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Saki Takamachi | + +----------------------------------------------------------------------+ +*/ + +#include "bcmath.h" +#include "private.h" +#include + +void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result) +{ + /* clear result */ + bc_free_num(result); + + /* + * The following cases result in an early return: + * + * - When rounding to an integer part which is larger than the number + * e.g. Rounding 21.123 to 3 digits before the decimal point. + * - When rounding to a greater decimal precision then the number has, the number is unchanged + * e.g. Rounding 21.123 to 4 digits after the decimal point. + * - If the fractional part ends with zeros, the zeros are omitted and the number of digits in num is reduced. + * Meaning we might end up in the previous case. + */ + if (precision < 0 && num->n_len < (size_t) (-(precision + Z_L(1))) + 1) { + *result = bc_copy_num(BCG(_zero_)); + return; + } + /* Just like bcadd('1', '1', 4) becomes '2.0000', it pads with zeros at the end if necessary. */ + if (precision >= 0 && num->n_scale <= precision) { + if (num->n_scale == precision) { + *result = bc_copy_num(num); + } else if(num->n_scale < precision) { + *result = bc_new_num(num->n_len, precision); + (*result)->n_sign = num->n_sign; + memcpy((*result)->n_value, num->n_value, num->n_len + num->n_scale); + } + return; + } + + /* + * If the calculation result is a negative value, there is an early return, + * so no underflow will occur. + */ + size_t rounded_len = num->n_len + precision; + + /* + * Initialize result + * For example, if rounded_len is 0, it means trying to round 50 to 100 or 0. + * If the result of rounding is carried over, it will be added later, so first set it to 0 here. + */ + if (rounded_len == 0) { + *result = bc_copy_num(BCG(_zero_)); + } else { + *result = bc_new_num(num->n_len, precision > 0 ? precision : 0); + memcpy((*result)->n_value, num->n_value, rounded_len); + } + (*result)->n_sign = num->n_sign; + + const char *nptr = num->n_value + rounded_len; + + /* Check cases that can be determined without looping. */ + switch (mode) { + case PHP_ROUND_HALF_UP: + if (*nptr >= 5) { + goto up; + } else if (*nptr < 5) { + return; + } + break; + + case PHP_ROUND_HALF_DOWN: + case PHP_ROUND_HALF_EVEN: + case PHP_ROUND_HALF_ODD: + if (*nptr > 5) { + goto up; + } else if (*nptr < 5) { + return; + } + /* if *nptr == 5, we need to look-up further digits before making a decision. */ + break; + + case PHP_ROUND_CEILING: + if (num->n_sign != PLUS) { + return; + } else if (*nptr > 0) { + goto up; + } + /* if *nptr == 0, a loop is required for judgment. */ + break; + + case PHP_ROUND_FLOOR: + if (num->n_sign != MINUS) { + return; + } else if (*nptr > 0) { + goto up; + } + /* if *nptr == 0, a loop is required for judgment. */ + break; + + case PHP_ROUND_TOWARD_ZERO: + return; + + case PHP_ROUND_AWAY_FROM_ZERO: + if (*nptr > 0) { + goto up; + } + /* if *nptr == 0, a loop is required for judgment. */ + break; + + EMPTY_SWITCH_DEFAULT_CASE() + } + + /* Loop through the remaining digits. */ + size_t count = num->n_len + num->n_scale - rounded_len - 1; + nptr++; + while ((count > 0) && (*nptr == 0)) { + count--; + nptr++; + } + + if (count > 0) { + goto up; + } + + switch (mode) { + case PHP_ROUND_HALF_DOWN: + case PHP_ROUND_CEILING: + case PHP_ROUND_FLOOR: + case PHP_ROUND_AWAY_FROM_ZERO: + return; + + case PHP_ROUND_HALF_EVEN: + if (rounded_len == 0 || num->n_value[rounded_len - 1] % 2 == 0) { + return; + } + break; + + case PHP_ROUND_HALF_ODD: + if (rounded_len != 0 && num->n_value[rounded_len - 1] % 2 == 1) { + return; + } + break; + + EMPTY_SWITCH_DEFAULT_CASE() + } + +up: + { + bc_num tmp; + + if (rounded_len == 0) { + tmp = bc_new_num(num->n_len + 1, 0); + tmp->n_value[0] = 1; + tmp->n_sign = num->n_sign; + } else { + bc_num scaled_one = bc_new_num((*result)->n_len, (*result)->n_scale); + scaled_one->n_value[rounded_len - 1] = 1; + + tmp = _bc_do_add(*result, scaled_one, (*result)->n_scale); + tmp->n_sign = (*result)->n_sign; + bc_free_num(&scaled_one); + } + + bc_free_num(result); + *result = tmp; + } +} diff --git a/ext/bcmath/php_bcmath.h b/ext/bcmath/php_bcmath.h index 1045019d64ca9..af8745cdba74f 100644 --- a/ext/bcmath/php_bcmath.h +++ b/ext/bcmath/php_bcmath.h @@ -19,6 +19,7 @@ #include "libbcmath/src/bcmath.h" #include "zend_API.h" +#include "ext/standard/php_math_round_mode.h" extern zend_module_entry bcmath_module_entry; #define phpext_bcmath_ptr &bcmath_module_entry diff --git a/ext/bcmath/tests/bcceil.phpt b/ext/bcmath/tests/bcceil.phpt new file mode 100644 index 0000000000000..54def154fbd72 --- /dev/null +++ b/ext/bcmath/tests/bcceil.phpt @@ -0,0 +1,46 @@ +--TEST-- +bcceil() function +--EXTENSIONS-- +bcmath +--FILE-- + ', bcceil($num), "\n"; +} +?> +--EXPECT-- + 0 => 0 + 0.00 => 0 + -0 => 0 + -0.00 => 0 + 0.01 => 1 + 0.000000000000000000000000000000000000000001 => 1 + -0.01 => 0 + -0.000000000000000000000000000000000000000001 => 0 + 1 => 1 + 1.0000 => 1 + 1.0001 => 2 + 100000.000000000000000000000000000000000000000001 => 100001 + -1 => -1 + -1.0000 => -1 + -1.0001 => -1 +-100000.000000000000000000000000000000000000000001 => -100000 diff --git a/ext/bcmath/tests/bcceil_error.phpt b/ext/bcmath/tests/bcceil_error.phpt new file mode 100644 index 0000000000000..fd4c51ff6c586 --- /dev/null +++ b/ext/bcmath/tests/bcceil_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +bcceil() function with error +--EXTENSIONS-- +bcmath +--FILE-- +getMessage()."\n"; +} + +try { + bcceil('0.00.1'); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECT-- +bcceil(): Argument #1 ($num) is not well-formed +bcceil(): Argument #1 ($num) is not well-formed diff --git a/ext/bcmath/tests/bcfloor.phpt b/ext/bcmath/tests/bcfloor.phpt new file mode 100644 index 0000000000000..d274ef44ba16a --- /dev/null +++ b/ext/bcmath/tests/bcfloor.phpt @@ -0,0 +1,46 @@ +--TEST-- +bcfloor() function +--EXTENSIONS-- +bcmath +--FILE-- + ', bcfloor($num), "\n"; +} +?> +--EXPECT-- + 0 => 0 + 0.00 => 0 + -0 => 0 + -0.00 => 0 + 0.01 => 0 + 0.000000000000000000000000000000000000000001 => 0 + -0.01 => -1 + -0.000000000000000000000000000000000000000001 => -1 + 1 => 1 + 1.0000 => 1 + 1.0001 => 1 + 100000.000000000000000000000000000000000000000001 => 100000 + -1 => -1 + -1.0000 => -1 + -1.0001 => -2 +-100000.000000000000000000000000000000000000000001 => -100001 diff --git a/ext/bcmath/tests/bcfloor_error.phpt b/ext/bcmath/tests/bcfloor_error.phpt new file mode 100644 index 0000000000000..7578a5afe3b8d --- /dev/null +++ b/ext/bcmath/tests/bcfloor_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +bcfloor() function with error +--EXTENSIONS-- +bcmath +--FILE-- +getMessage()."\n"; +} + +try { + bcfloor('0.00.1'); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECT-- +bcfloor(): Argument #1 ($num) is not well-formed +bcfloor(): Argument #1 ($num) is not well-formed diff --git a/ext/bcmath/tests/bcround_away_from_zero.phpt b/ext/bcmath/tests/bcround_away_from_zero.phpt new file mode 100644 index 0000000000000..cfaa15002585e --- /dev/null +++ b/ext/bcmath/tests/bcround_away_from_zero.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_AWAY_FROM_ZERO +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 2 + [1.2, 0] => 2 + [1.3, 0] => 2 + [1.4, 0] => 2 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -2 + [-1.2, 0] => -2 + [-1.3, 0] => -2 + [-1.4, 0] => -2 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 100 + [-50, -2] => -100 + [1230, -1] => 1230 + [1235, -1] => 1240 + [-1230, -1] => -1230 + [-1235, -1] => -1240 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3500 + [3450.0000, -2] => 3500 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3500 + [-3450.0000, -2] => -3500 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1236 + [1235.5, 0] => 1236 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1236 + [-1235.5, 0] => -1236 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 1 + [0.5, 0] => 1 + [0.5000, 0] => 1 + [0.5001, 0] => 1 + [-0.0001, 0] => -1 + [-0.5, 0] => -1 + [-0.5000, 0] => -1 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.5 + [28.45, 1] => 28.5 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.5 + [-28.45, 1] => -28.5 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 154.0 + [153.95, 1] => 154.0 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -154.0 + [-153.95, 1] => -154.0 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.001 + [0.0005, 3] => 0.001 + [0.000500, 3] => 0.001 + [0.000501, 3] => 0.001 + [-0.000001, 3] => -0.001 + [-0.0005, 3] => -0.001 + [-0.000500, 3] => -0.001 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_ceiling.phpt b/ext/bcmath/tests/bcround_ceiling.phpt new file mode 100644 index 0000000000000..a3f6b50e1f989 --- /dev/null +++ b/ext/bcmath/tests/bcround_ceiling.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_CEILING +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 2 + [1.2, 0] => 2 + [1.3, 0] => 2 + [1.4, 0] => 2 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -1 + [-1.7, 0] => -1 + [-1.8, 0] => -1 + [-1.9, 0] => -1 + +========== minus precision ========== + [50, -2] => 100 + [-50, -2] => 0 + [1230, -1] => 1230 + [1235, -1] => 1240 + [-1230, -1] => -1230 + [-1235, -1] => -1230 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3500 + [3450.0000, -2] => 3500 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3400 + [-3450.0001, -2] => -3400 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1236 + [1235.5, 0] => 1236 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1235 +[-1235.500001, 0] => -1235 + [0.0001, 0] => 1 + [0.5, 0] => 1 + [0.5000, 0] => 1 + [0.5001, 0] => 1 + [-0.0001, 0] => 0 + [-0.5, 0] => 0 + [-0.5000, 0] => 0 + [-0.5001, 0] => 0 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.5 + [28.45, 1] => 28.5 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.4 + [-28.4500001, 1] => -28.4 + [153.90, 1] => 153.9 + [153.9000001, 1] => 154.0 + [153.95, 1] => 154.0 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -153.9 +[-153.9500001, 1] => -153.9 + [0.000001, 3] => 0.001 + [0.0005, 3] => 0.001 + [0.000500, 3] => 0.001 + [0.000501, 3] => 0.001 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => 0.000 + [-0.000500, 3] => 0.000 + [-0.000501, 3] => 0.000 diff --git a/ext/bcmath/tests/bcround_early_return.phpt b/ext/bcmath/tests/bcround_early_return.phpt new file mode 100644 index 0000000000000..372545a22ffa1 --- /dev/null +++ b/ext/bcmath/tests/bcround_early_return.phpt @@ -0,0 +1,99 @@ +--TEST-- +bcround() function with early return +--EXTENSIONS-- +bcmath +--FILE-- + [], + 'PHP_ROUND_HALF_DOWN' => [], + 'PHP_ROUND_HALF_EVEN' => [], + 'PHP_ROUND_HALF_ODD' => [], + 'PHP_ROUND_FLOOR' => [], + 'PHP_ROUND_CEIL' => [], + 'PHP_ROUND_AWAY_FROM_ZERO' => [], + 'PHP_ROUND_TOWARD_ZERO' => [], +]; +foreach ($early_return_cases as [$num, $precision]) { + $result = str_pad("[{$num}, {$precision}]", 33, ' ', STR_PAD_LEFT) . ' => ' . bcround($num, $precision, PHP_ROUND_HALF_UP) . "\n"; + echo $result; + $results['PHP_ROUND_HALF_UP'][] = $result; +} + +echo "\n"; + +foreach ($otherModes as $mode) { + foreach ($early_return_cases as [$num, $precision]) { + $result = str_pad("[{$num}, {$precision}]", 33, ' ', STR_PAD_LEFT) . ' => ' . bcround($num, $precision, constant($mode)) . "\n"; + $results[$mode][] = $result; + } + + if ($results['PHP_ROUND_HALF_UP'] === $results[$mode]) { + echo str_pad($mode, 24, ' ', STR_PAD_LEFT) . ": result is same to PHP_ROUND_HALF_UP\n"; + } else { + echo str_pad($mode, 24, ' ', STR_PAD_LEFT) . ": result is not same to PHP_ROUND_HALF_UP, failed\n"; + } +} +?> +--EXPECT-- + [123, -4] => 0 + [123.123456, -4] => 0 + [123, 1] => 123.0 + [123.5, 1] => 123.5 + [123.5, 2] => 123.50 + [123.0000000000000000000001, 22] => 123.0000000000000000000001 + [123.0000000000000000000001, 23] => 123.00000000000000000000010 + [-123, -4] => 0 + [-123.123456, -4] => 0 + [-123, 1] => -123.0 + [-123.5, 1] => -123.5 + [-123.5, 2] => -123.50 +[-123.0000000000000000000001, 22] => -123.0000000000000000000001 +[-123.0000000000000000000001, 23] => -123.00000000000000000000010 + [0, 0] => 0 + [0.0, 0] => 0 + [0.0000, 0] => 0 + [-0, 0] => 0 + [-0.0, 0] => 0 + [-0.0000, 0] => 0 + + PHP_ROUND_HALF_DOWN: result is same to PHP_ROUND_HALF_UP + PHP_ROUND_HALF_EVEN: result is same to PHP_ROUND_HALF_UP + PHP_ROUND_HALF_ODD: result is same to PHP_ROUND_HALF_UP + PHP_ROUND_FLOOR: result is same to PHP_ROUND_HALF_UP + PHP_ROUND_CEILING: result is same to PHP_ROUND_HALF_UP +PHP_ROUND_AWAY_FROM_ZERO: result is same to PHP_ROUND_HALF_UP + PHP_ROUND_TOWARD_ZERO: result is same to PHP_ROUND_HALF_UP diff --git a/ext/bcmath/tests/bcround_error.phpt b/ext/bcmath/tests/bcround_error.phpt new file mode 100644 index 0000000000000..d411763b49326 --- /dev/null +++ b/ext/bcmath/tests/bcround_error.phpt @@ -0,0 +1,28 @@ +--TEST-- +bcround() function with error +--EXTENSIONS-- +bcmath +--FILE-- +getMessage()."\n"; +} + +try { + bcround('0.00.1'); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} + +try { + bcround('0.001', 0, 1000); +} catch (Throwable $e) { + echo $e->getMessage()."\n"; +} +?> +--EXPECT-- +bcround(): Argument #1 ($num) is not well-formed +bcround(): Argument #1 ($num) is not well-formed +bcround(): Argument #3 ($mode) must be a valid rounding mode (PHP_ROUND_*) diff --git a/ext/bcmath/tests/bcround_floor.phpt b/ext/bcmath/tests/bcround_floor.phpt new file mode 100644 index 0000000000000..88d7fcd9dbb29 --- /dev/null +++ b/ext/bcmath/tests/bcround_floor.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_FLOOR +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 1 + [1.7, 0] => 1 + [1.8, 0] => 1 + [1.9, 0] => 1 + [-1.1, 0] => -2 + [-1.2, 0] => -2 + [-1.3, 0] => -2 + [-1.4, 0] => -2 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 0 + [-50, -2] => -100 + [1230, -1] => 1230 + [1235, -1] => 1230 + [-1230, -1] => -1230 + [-1235, -1] => -1240 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3400 + [3450.0001, -2] => 3400 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3500 + [-3450.0000, -2] => -3500 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1235 + [1235.500001, 0] => 1235 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1236 + [-1235.5, 0] => -1236 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 0 + [0.5, 0] => 0 + [0.5000, 0] => 0 + [0.5001, 0] => 0 + [-0.0001, 0] => -1 + [-0.5, 0] => -1 + [-0.5000, 0] => -1 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.4 + [28.4500001, 1] => 28.4 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.5 + [-28.45, 1] => -28.5 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 153.9 + [153.9500001, 1] => 153.9 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -154.0 + [-153.95, 1] => -154.0 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.000 + [0.000500, 3] => 0.000 + [0.000501, 3] => 0.000 + [-0.000001, 3] => -0.001 + [-0.0005, 3] => -0.001 + [-0.000500, 3] => -0.001 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_half_down.phpt b/ext/bcmath/tests/bcround_half_down.phpt new file mode 100644 index 0000000000000..03d3580248111 --- /dev/null +++ b/ext/bcmath/tests/bcround_half_down.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_HALF_DOWN +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 0 + [-50, -2] => 0 + [1230, -1] => 1230 + [1235, -1] => 1230 + [-1230, -1] => -1230 + [-1235, -1] => -1230 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3400 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3400 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1235 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1235 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 0 + [0.5, 0] => 0 + [0.5000, 0] => 0 + [0.5001, 0] => 1 + [-0.0001, 0] => 0 + [-0.5, 0] => 0 + [-0.5000, 0] => 0 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.4 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.4 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 153.9 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -153.9 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.000 + [0.000500, 3] => 0.000 + [0.000501, 3] => 0.001 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => 0.000 + [-0.000500, 3] => 0.000 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_half_even.phpt b/ext/bcmath/tests/bcround_half_even.phpt new file mode 100644 index 0000000000000..955f0e14dcfd1 --- /dev/null +++ b/ext/bcmath/tests/bcround_half_even.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_HALF_EVEN +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 0 + [-50, -2] => 0 + [1230, -1] => 1230 + [1235, -1] => 1240 + [-1230, -1] => -1230 + [-1235, -1] => -1240 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3400 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3400 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1236 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1236 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 0 + [0.5, 0] => 0 + [0.5000, 0] => 0 + [0.5001, 0] => 1 + [-0.0001, 0] => 0 + [-0.5, 0] => 0 + [-0.5000, 0] => 0 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.4 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.4 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 154.0 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -154.0 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.000 + [0.000500, 3] => 0.000 + [0.000501, 3] => 0.001 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => 0.000 + [-0.000500, 3] => 0.000 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_half_odd.phpt b/ext/bcmath/tests/bcround_half_odd.phpt new file mode 100644 index 0000000000000..941da75843f29 --- /dev/null +++ b/ext/bcmath/tests/bcround_half_odd.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_HALF_ODD +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 100 + [-50, -2] => -100 + [1230, -1] => 1230 + [1235, -1] => 1230 + [-1230, -1] => -1230 + [-1235, -1] => -1230 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3500 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3500 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1235 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1235 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 0 + [0.5, 0] => 1 + [0.5000, 0] => 1 + [0.5001, 0] => 1 + [-0.0001, 0] => 0 + [-0.5, 0] => -1 + [-0.5000, 0] => -1 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.5 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.5 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 153.9 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -153.9 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.001 + [0.000500, 3] => 0.001 + [0.000501, 3] => 0.001 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => -0.001 + [-0.000500, 3] => -0.001 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_half_up.phpt b/ext/bcmath/tests/bcround_half_up.phpt new file mode 100644 index 0000000000000..17c02c717e3a2 --- /dev/null +++ b/ext/bcmath/tests/bcround_half_up.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_HALF_UP +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 2 + [1.7, 0] => 2 + [1.8, 0] => 2 + [1.9, 0] => 2 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -2 + [-1.7, 0] => -2 + [-1.8, 0] => -2 + [-1.9, 0] => -2 + +========== minus precision ========== + [50, -2] => 100 + [-50, -2] => -100 + [1230, -1] => 1230 + [1235, -1] => 1240 + [-1230, -1] => -1230 + [-1235, -1] => -1240 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3500 + [3450.0001, -2] => 3500 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3500 + [-3450.0001, -2] => -3500 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1236 + [1235.500001, 0] => 1236 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1236 +[-1235.500001, 0] => -1236 + [0.0001, 0] => 0 + [0.5, 0] => 1 + [0.5000, 0] => 1 + [0.5001, 0] => 1 + [-0.0001, 0] => 0 + [-0.5, 0] => -1 + [-0.5000, 0] => -1 + [-0.5001, 0] => -1 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.5 + [28.4500001, 1] => 28.5 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.5 + [-28.4500001, 1] => -28.5 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 154.0 + [153.9500001, 1] => 154.0 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -154.0 +[-153.9500001, 1] => -154.0 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.001 + [0.000500, 3] => 0.001 + [0.000501, 3] => 0.001 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => -0.001 + [-0.000500, 3] => -0.001 + [-0.000501, 3] => -0.001 diff --git a/ext/bcmath/tests/bcround_test_helper.inc b/ext/bcmath/tests/bcround_test_helper.inc new file mode 100644 index 0000000000000..c51efc46581b3 --- /dev/null +++ b/ext/bcmath/tests/bcround_test_helper.inc @@ -0,0 +1,108 @@ + ", bcround($num, $precision, $mode), "\n"; + } + echo "\n"; +} + +function run_round_test(int $mode) +{ + $non_boundary_value_cases = [ + ['1.1', 0], + ['1.2', 0], + ['1.3', 0], + ['1.4', 0], + ['1.6', 0], + ['1.7', 0], + ['1.8', 0], + ['1.9', 0], + ['-1.1', 0], + ['-1.2', 0], + ['-1.3', 0], + ['-1.4', 0], + ['-1.6', 0], + ['-1.7', 0], + ['-1.8', 0], + ['-1.9', 0], + ]; + + $minus_precision_cases = [ + ['50', -2], + ['-50', -2], + ['1230', -1], + ['1235', -1], + ['-1230', -1], + ['-1235', -1], + ['3400.0000', -2], + ['3400.0001', -2], + ['3450.0000', -2], + ['3450.0001', -2], + ['-3400.0000', -2], + ['-3400.0001', -2], + ['-3450.0000', -2], + ['-3450.0001', -2], + ]; + + $zero_precision_cases = [ + ['1235', 0], + ['1235.0', 0], + ['1235.000001', 0], + ['1235.5', 0], + ['1235.500001', 0], + ['-1235', 0], + ['-1235.0', 0], + ['-1235.000001', 0], + ['-1235.5', 0], + ['-1235.500001', 0], + ['0.0001', 0], + ['0.5', 0], + ['0.5000', 0], + ['0.5001', 0], + ['-0.0001', 0], + ['-0.5', 0], + ['-0.5000', 0], + ['-0.5001', 0], + ]; + + $plus_precision_cases = [ + ['28.40', 1], + ['28.4000001', 1], + ['28.45', 1], + ['28.4500001', 1], + ['-28.40', 1], + ['-28.4000001', 1], + ['-28.45', 1], + ['-28.4500001', 1], + ['153.90', 1], + ['153.9000001', 1], + ['153.95', 1], + ['153.9500001', 1], + ['-153.90', 1], + ['-153.9000001', 1], + ['-153.95', 1], + ['-153.9500001', 1], + ['0.000001', 3], + ['0.0005', 3], + ['0.000500', 3], + ['0.000501', 3], + ['-0.000001', 3], + ['-0.0005', 3], + ['-0.000500', 3], + ['-0.000501', 3], + ]; + + echo "========== non-boundary value ==========\n"; + printResult($non_boundary_value_cases, $mode); + + echo "========== minus precision ==========\n"; + printResult($minus_precision_cases, $mode); + + echo "========== zero precision ==========\n"; + printResult($zero_precision_cases, $mode); + + echo "========== plus precision ==========\n"; + printResult($plus_precision_cases, $mode); +} diff --git a/ext/bcmath/tests/bcround_toward_zero.phpt b/ext/bcmath/tests/bcround_toward_zero.phpt new file mode 100644 index 0000000000000..e9d0e277efe6e --- /dev/null +++ b/ext/bcmath/tests/bcround_toward_zero.phpt @@ -0,0 +1,89 @@ +--TEST-- +bcround() function PHP_ROUND_TOWARD_ZERO +--EXTENSIONS-- +bcmath +--FILE-- + +--EXPECT-- +========== non-boundary value ========== + [1.1, 0] => 1 + [1.2, 0] => 1 + [1.3, 0] => 1 + [1.4, 0] => 1 + [1.6, 0] => 1 + [1.7, 0] => 1 + [1.8, 0] => 1 + [1.9, 0] => 1 + [-1.1, 0] => -1 + [-1.2, 0] => -1 + [-1.3, 0] => -1 + [-1.4, 0] => -1 + [-1.6, 0] => -1 + [-1.7, 0] => -1 + [-1.8, 0] => -1 + [-1.9, 0] => -1 + +========== minus precision ========== + [50, -2] => 0 + [-50, -2] => 0 + [1230, -1] => 1230 + [1235, -1] => 1230 + [-1230, -1] => -1230 + [-1235, -1] => -1230 + [3400.0000, -2] => 3400 + [3400.0001, -2] => 3400 + [3450.0000, -2] => 3400 + [3450.0001, -2] => 3400 + [-3400.0000, -2] => -3400 + [-3400.0001, -2] => -3400 + [-3450.0000, -2] => -3400 + [-3450.0001, -2] => -3400 + +========== zero precision ========== + [1235, 0] => 1235 + [1235.0, 0] => 1235 + [1235.000001, 0] => 1235 + [1235.5, 0] => 1235 + [1235.500001, 0] => 1235 + [-1235, 0] => -1235 + [-1235.0, 0] => -1235 +[-1235.000001, 0] => -1235 + [-1235.5, 0] => -1235 +[-1235.500001, 0] => -1235 + [0.0001, 0] => 0 + [0.5, 0] => 0 + [0.5000, 0] => 0 + [0.5001, 0] => 0 + [-0.0001, 0] => 0 + [-0.5, 0] => 0 + [-0.5000, 0] => 0 + [-0.5001, 0] => 0 + +========== plus precision ========== + [28.40, 1] => 28.4 + [28.4000001, 1] => 28.4 + [28.45, 1] => 28.4 + [28.4500001, 1] => 28.4 + [-28.40, 1] => -28.4 + [-28.4000001, 1] => -28.4 + [-28.45, 1] => -28.4 + [-28.4500001, 1] => -28.4 + [153.90, 1] => 153.9 + [153.9000001, 1] => 153.9 + [153.95, 1] => 153.9 + [153.9500001, 1] => 153.9 + [-153.90, 1] => -153.9 +[-153.9000001, 1] => -153.9 + [-153.95, 1] => -153.9 +[-153.9500001, 1] => -153.9 + [0.000001, 3] => 0.000 + [0.0005, 3] => 0.000 + [0.000500, 3] => 0.000 + [0.000501, 3] => 0.000 + [-0.000001, 3] => 0.000 + [-0.0005, 3] => 0.000 + [-0.000500, 3] => 0.000 + [-0.000501, 3] => 0.000 diff --git a/ext/standard/php_math.h b/ext/standard/php_math.h index 6ff895cce3c8d..9ebd600537fdb 100644 --- a/ext/standard/php_math.h +++ b/ext/standard/php_math.h @@ -28,6 +28,7 @@ PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret); PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base); #include +#include "php_math_round_mode.h" #ifndef M_E #define M_E 2.7182818284590452354 /* e */ @@ -97,37 +98,4 @@ PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base); #define M_SQRT3 1.73205080756887729352 /* sqrt(3) */ #endif -/* Define rounding modes (all are round-to-nearest) */ -#ifndef PHP_ROUND_HALF_UP -#define PHP_ROUND_HALF_UP 0x01 /* Arithmetic rounding, up == away from zero */ -#endif - -#ifndef PHP_ROUND_HALF_DOWN -#define PHP_ROUND_HALF_DOWN 0x02 /* Arithmetic rounding, down == towards zero */ -#endif - -#ifndef PHP_ROUND_HALF_EVEN -#define PHP_ROUND_HALF_EVEN 0x03 /* Banker's rounding */ -#endif - -#ifndef PHP_ROUND_HALF_ODD -#define PHP_ROUND_HALF_ODD 0x04 -#endif - -#ifndef PHP_ROUND_CEILING -#define PHP_ROUND_CEILING 0x05 -#endif - -#ifndef PHP_ROUND_FLOOR -#define PHP_ROUND_FLOOR 0x06 -#endif - -#ifndef PHP_ROUND_TOWARD_ZERO -#define PHP_ROUND_TOWARD_ZERO 0x07 -#endif - -#ifndef PHP_ROUND_AWAY_FROM_ZERO -#define PHP_ROUND_AWAY_FROM_ZERO 0x08 -#endif - #endif /* PHP_MATH_H */ diff --git a/ext/standard/php_math_round_mode.h b/ext/standard/php_math_round_mode.h new file mode 100644 index 0000000000000..d4c11a8312fb2 --- /dev/null +++ b/ext/standard/php_math_round_mode.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jim Winstead | + | Stig Sæther Bakken | + +----------------------------------------------------------------------+ +*/ + +/* Define rounding modes (all are round-to-nearest) */ +#ifndef PHP_ROUND_HALF_UP +#define PHP_ROUND_HALF_UP 0x01 /* Arithmetic rounding, up == away from zero */ +#endif + +#ifndef PHP_ROUND_HALF_DOWN +#define PHP_ROUND_HALF_DOWN 0x02 /* Arithmetic rounding, down == towards zero */ +#endif + +#ifndef PHP_ROUND_HALF_EVEN +#define PHP_ROUND_HALF_EVEN 0x03 /* Banker's rounding */ +#endif + +#ifndef PHP_ROUND_HALF_ODD +#define PHP_ROUND_HALF_ODD 0x04 +#endif + +#ifndef PHP_ROUND_CEILING +#define PHP_ROUND_CEILING 0x05 +#endif + +#ifndef PHP_ROUND_FLOOR +#define PHP_ROUND_FLOOR 0x06 +#endif + +#ifndef PHP_ROUND_TOWARD_ZERO +#define PHP_ROUND_TOWARD_ZERO 0x07 +#endif + +#ifndef PHP_ROUND_AWAY_FROM_ZERO +#define PHP_ROUND_AWAY_FROM_ZERO 0x08 +#endif