1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

RFC: Add the RoundingMode enum (#14833)

see https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum

Co-authored-by: Saki Takamachi <saki@php.net>
Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
This commit is contained in:
Tim Düsterhus
2024-07-18 20:44:30 +02:00
committed by GitHub
parent 81da53fccd
commit 5905857fd2
29 changed files with 401 additions and 152 deletions

5
NEWS
View File

@@ -5,6 +5,10 @@ PHP NEWS
- Core:
. Fix GH-14978 (The xmlreader extension phpize build). (Peter Kokot)
- BCMath:
. Adjust bcround()'s $mode parameter to only accept the RoundingMode
enum. (timwolla, saki)
- DOM:
. Fix trampoline leak in xpath callables. (nielsdos)
@@ -18,6 +22,7 @@ PHP NEWS
- Standard:
. Fix references in request_parse_body() options array. (nielsdos)
. Add RoundingMode enum. (timwolla, saki)
- XSL:
. Fix trampoline leak in xpath callables. (nielsdos)

View File

@@ -344,6 +344,9 @@ PHP 8.4 UPGRADE NOTES
. stream_bucket_make_writeable() and stream_bucket_new() will now return a
StreamBucket instance instead of an stdClass instance.
RFC: https://wiki.php.net/rfc/dedicated_stream_bucket
. Added a new RoundingMode enum with clearer naming and improved discoverability
compared to the PHP_ROUND_* constants.
RFC: https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum
- SOAP:
. Added support for clark notation for namespaces in class map.
@@ -549,23 +552,27 @@ PHP 8.4 UPGRADE NOTES
would have resulted in 1.0 instead of the correct result 0.0. Additional
inputs might also be affected and result in different outputs compared to
earlier PHP versions.
. The default value of the 'cost' option for PASSWORD_BCRYPT for password_hash()
has been increased from '10' to '12'.
. The $mode parameter of the round() function has been widened to RoundingMode|int,
accepting instances of a new RoundingMode enum.
RFC: https://wiki.php.net/rfc/bcrypt_cost_2023
. Four new modes have been added to the round() function: PHP_ROUND_CEILING,
PHP_ROUND_FLOOR, PHP_ROUND_TOWARD_ZERO, PHP_ROUND_AWAY_FROM_ZERO.
RFC: https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum
. Four new modes have been added to the round() function: RoundingMode::PositiveInfinity,
RoundingMode::NegativeInfinity, RoundingMode::TowardsZero, RoundingMode::AwayFromZero.
RFC: https://wiki.php.net/rfc/new_rounding_modes_to_round_function
. debug_zval_dump() now indicates whether an array is packed.
. Fixed a bug caused by "pre-rounding" of the round() function. Previously, using
"pre-rounding" to treat a value like 0.285 (actually 0.28499999999999998) as a
decimal number and round it to 0.29. However, "pre-rounding" incorrectly rounds
certain numbers, so this fix removes "pre-rounding" and changes the way numbers
are compared, so that the values are correctly rounded as decimal numbers.
. long2ip() now returns string instead of string|false.
. The maximum precision that can be handled by round() has been extended by
one digit.
. The default value of the 'cost' option for PASSWORD_BCRYPT for password_hash()
has been increased from '10' to '12'.
RFC: https://wiki.php.net/rfc/bcrypt_cost_2023
. debug_zval_dump() now indicates whether an array is packed.
. long2ip() now returns string instead of string|false.
. output_add_rewrite_var() now uses url_rewriter.hosts instead of
session.trans_sid_hosts for selecting hosts that will be rewritten.
. highlight_string() now has a return type of string|true instead of string|bool.
@@ -809,12 +816,6 @@ PHP 8.4 UPGRADE NOTES
. P_SID (NetBSD/FreeBSD only).
. P_JAILID (FreeBSD only).
- Standard:
. PHP_ROUND_CEILING.
. PHP_ROUND_FLOOR.
. PHP_ROUND_TOWARD_ZERO.
. PHP_ROUND_AWAY_FROM_ZERO.
- Sockets:
. SO_EXCLUSIVEADDRUSE (Windows only).
. SOCK_CONN_DGRAM (NetBSD only).

View File

@@ -696,15 +696,20 @@ PHP_FUNCTION(bcround)
zend_string *numstr;
zend_long precision = 0;
zend_long mode = PHP_ROUND_HALF_UP;
zend_object *mode_object = NULL;
bc_num num = NULL, result;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STR(numstr)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(precision)
Z_PARAM_LONG(mode)
Z_PARAM_OBJ_OF_CLASS(mode_object, rounding_mode_ce)
ZEND_PARSE_PARAMETERS_END();
if (mode_object != NULL) {
mode = php_math_round_mode_from_enum(mode_object);
}
switch (mode) {
case PHP_ROUND_HALF_UP:
case PHP_ROUND_HALF_DOWN:
@@ -716,7 +721,8 @@ PHP_FUNCTION(bcround)
case PHP_ROUND_AWAY_FROM_ZERO:
break;
default:
zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
/* This is currently unreachable, but might become reachable when new modes are added. */
zend_argument_value_error(3, "is an unsupported rounding mode");
return;
}

View File

@@ -37,4 +37,4 @@ function bcfloor(string $num): string {}
function bcceil(string $num): string {}
/** @refcount 1 */
function bcround(string $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): string {}
function bcround(string $num, int $precision = 0, RoundingMode $mode = RoundingMode::HalfAwayFromZero): string {}

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: cd3d182e13cb0ca22b27c13a8d0a86c20fde5b76 */
* Stub hash: 7439a5fca649b8c4df317b0da1416c8fe59faf72 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bcadd, 0, 2, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, num1, IS_STRING, 0)
@@ -52,7 +52,7 @@ ZEND_END_ARG_INFO()
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_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, mode, RoundingMode, 0, "RoundingMode::HalfAwayFromZero")
ZEND_END_ARG_INFO()
ZEND_FUNCTION(bcadd);

View File

@@ -0,0 +1,104 @@
--TEST--
bcround() function all modes
--EXTENSIONS--
bcmath
--FILE--
<?php
foreach (RoundingMode::cases() as $mode) {
foreach ([
'1.0',
'-1.0',
'1.2',
'-1.2',
'1.7',
'-1.7',
'1.5',
'-1.5',
'2.5',
'-2.5',
] as $number) {
printf("%-20s: %s -> %s\n", $mode->name, $number, bcround($number, 0, $mode));
}
}
?>
--EXPECT--
HalfAwayFromZero : 1.0 -> 1
HalfAwayFromZero : -1.0 -> -1
HalfAwayFromZero : 1.2 -> 1
HalfAwayFromZero : -1.2 -> -1
HalfAwayFromZero : 1.7 -> 2
HalfAwayFromZero : -1.7 -> -2
HalfAwayFromZero : 1.5 -> 2
HalfAwayFromZero : -1.5 -> -2
HalfAwayFromZero : 2.5 -> 3
HalfAwayFromZero : -2.5 -> -3
HalfTowardsZero : 1.0 -> 1
HalfTowardsZero : -1.0 -> -1
HalfTowardsZero : 1.2 -> 1
HalfTowardsZero : -1.2 -> -1
HalfTowardsZero : 1.7 -> 2
HalfTowardsZero : -1.7 -> -2
HalfTowardsZero : 1.5 -> 1
HalfTowardsZero : -1.5 -> -1
HalfTowardsZero : 2.5 -> 2
HalfTowardsZero : -2.5 -> -2
HalfEven : 1.0 -> 1
HalfEven : -1.0 -> -1
HalfEven : 1.2 -> 1
HalfEven : -1.2 -> -1
HalfEven : 1.7 -> 2
HalfEven : -1.7 -> -2
HalfEven : 1.5 -> 2
HalfEven : -1.5 -> -2
HalfEven : 2.5 -> 2
HalfEven : -2.5 -> -2
HalfOdd : 1.0 -> 1
HalfOdd : -1.0 -> -1
HalfOdd : 1.2 -> 1
HalfOdd : -1.2 -> -1
HalfOdd : 1.7 -> 2
HalfOdd : -1.7 -> -2
HalfOdd : 1.5 -> 1
HalfOdd : -1.5 -> -1
HalfOdd : 2.5 -> 3
HalfOdd : -2.5 -> -3
TowardsZero : 1.0 -> 1
TowardsZero : -1.0 -> -1
TowardsZero : 1.2 -> 1
TowardsZero : -1.2 -> -1
TowardsZero : 1.7 -> 1
TowardsZero : -1.7 -> -1
TowardsZero : 1.5 -> 1
TowardsZero : -1.5 -> -1
TowardsZero : 2.5 -> 2
TowardsZero : -2.5 -> -2
AwayFromZero : 1.0 -> 1
AwayFromZero : -1.0 -> -1
AwayFromZero : 1.2 -> 2
AwayFromZero : -1.2 -> -2
AwayFromZero : 1.7 -> 2
AwayFromZero : -1.7 -> -2
AwayFromZero : 1.5 -> 2
AwayFromZero : -1.5 -> -2
AwayFromZero : 2.5 -> 3
AwayFromZero : -2.5 -> -3
NegativeInfinity : 1.0 -> 1
NegativeInfinity : -1.0 -> -1
NegativeInfinity : 1.2 -> 1
NegativeInfinity : -1.2 -> -2
NegativeInfinity : 1.7 -> 1
NegativeInfinity : -1.7 -> -2
NegativeInfinity : 1.5 -> 1
NegativeInfinity : -1.5 -> -2
NegativeInfinity : 2.5 -> 2
NegativeInfinity : -2.5 -> -3
PositiveInfinity : 1.0 -> 1
PositiveInfinity : -1.0 -> -1
PositiveInfinity : 1.2 -> 2
PositiveInfinity : -1.2 -> -1
PositiveInfinity : 1.7 -> 2
PositiveInfinity : -1.7 -> -1
PositiveInfinity : 1.5 -> 2
PositiveInfinity : -1.5 -> -1
PositiveInfinity : 2.5 -> 3
PositiveInfinity : -2.5 -> -2

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_AWAY_FROM_ZERO
bcround() function AwayFromZero
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_AWAY_FROM_ZERO);
run_round_test(RoundingMode::AwayFromZero);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_CEILING
bcround() function PositiveInfinity
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_CEILING);
run_round_test(RoundingMode::PositiveInfinity);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -4,15 +4,6 @@ bcround() function with early return
bcmath
--FILE--
<?php
$otherModes = [
'PHP_ROUND_HALF_DOWN',
'PHP_ROUND_HALF_EVEN',
'PHP_ROUND_HALF_ODD',
'PHP_ROUND_FLOOR',
'PHP_ROUND_CEILING',
'PHP_ROUND_AWAY_FROM_ZERO',
'PHP_ROUND_TOWARD_ZERO',
];
$early_return_cases = [
['123', -4],
@@ -38,33 +29,27 @@ $early_return_cases = [
];
$results = [
'PHP_ROUND_HALF_UP' => [],
'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' => [],
RoundingMode::HalfAwayFromZero->name => [],
];
foreach ($early_return_cases as [$num, $precision]) {
$result = str_pad("[{$num}, {$precision}]", 33, ' ', STR_PAD_LEFT) . ' => ' . bcround($num, $precision, PHP_ROUND_HALF_UP) . "\n";
$result = str_pad("[{$num}, {$precision}]", 33, ' ', STR_PAD_LEFT) . ' => ' . bcround($num, $precision, RoundingMode::HalfAwayFromZero) . "\n";
echo $result;
$results['PHP_ROUND_HALF_UP'][] = $result;
$results[RoundingMode::HalfAwayFromZero->name][] = $result;
}
echo "\n";
foreach ($otherModes as $mode) {
foreach (RoundingMode::cases() as $mode) {
$results[$mode->name] = [];
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;
$result = str_pad("[{$num}, {$precision}]", 33, ' ', STR_PAD_LEFT) . ' => ' . bcround($num, $precision, $mode) . "\n";
$results[$mode->name][] = $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";
if ($results[RoundingMode::HalfAwayFromZero->name] === $results[$mode->name]) {
echo str_pad($mode->name, 24, ' ', STR_PAD_LEFT) . ": result is same to HalfAwayFromZero\n";
} else {
echo str_pad($mode, 24, ' ', STR_PAD_LEFT) . ": result is not same to PHP_ROUND_HALF_UP, failed\n";
echo str_pad($mode->name, 24, ' ', STR_PAD_LEFT) . ": result is not same to HalfAwayFromZero, failed\n";
}
}
?>
@@ -90,10 +75,11 @@ foreach ($otherModes as $mode) {
[-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
HalfAwayFromZero: result is same to HalfAwayFromZero
HalfTowardsZero: result is same to HalfAwayFromZero
HalfEven: result is same to HalfAwayFromZero
HalfOdd: result is same to HalfAwayFromZero
TowardsZero: result is same to HalfAwayFromZero
AwayFromZero: result is same to HalfAwayFromZero
NegativeInfinity: result is same to HalfAwayFromZero
PositiveInfinity: result is same to HalfAwayFromZero

View File

@@ -15,14 +15,7 @@ try {
} 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_*)

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_FLOOR
bcround() function NegativeInfinity
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_FLOOR);
run_round_test(RoundingMode::NegativeInfinity);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_HALF_DOWN
bcround() function HalfTowardsZero
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_HALF_DOWN);
run_round_test(RoundingMode::HalfTowardsZero);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_HALF_EVEN
bcround() function HalfEven
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_HALF_EVEN);
run_round_test(RoundingMode::HalfEven);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_HALF_ODD
bcround() function HalfOdd
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_HALF_ODD);
run_round_test(RoundingMode::HalfOdd);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_HALF_UP
bcround() function HalfAwayFromZero
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_HALF_UP);
run_round_test(RoundingMode::HalfAwayFromZero);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -1,6 +1,6 @@
<?php
function printResult (array $cases, int $mode)
function printResult (array $cases, RoundingMode $mode)
{
foreach ($cases as [$num, $precision]) {
echo str_pad("[{$num}, {$precision}]", 17, ' ', STR_PAD_LEFT), " => ", bcround($num, $precision, $mode), "\n";
@@ -8,7 +8,7 @@ function printResult (array $cases, int $mode)
echo "\n";
}
function run_round_test(int $mode)
function run_round_test(RoundingMode $mode)
{
$non_boundary_value_cases = [
['1.1', 0],

View File

@@ -1,11 +1,11 @@
--TEST--
bcround() function PHP_ROUND_TOWARD_ZERO
bcround() function TowardsZero
--EXTENSIONS--
bcmath
--FILE--
<?php
require_once __DIR__ . '/bcround_test_helper.inc';
run_round_test(PHP_ROUND_TOWARD_ZERO);
run_round_test(RoundingMode::TowardsZero);
?>
--EXPECT--
========== non-boundary value ==========

View File

@@ -5,18 +5,16 @@ Felix De Vliegher <felix.devliegher@gmail.com>
--FILE--
<?php
$standard = new ReflectionExtension('standard');
var_dump($standard->getClassNames());
$classNames = $standard->getClassNames();
sort($classNames);
foreach ($classNames as $className) {
echo $className, PHP_EOL;
}
?>
--EXPECT--
array(5) {
[0]=>
string(22) "__PHP_Incomplete_Class"
[1]=>
string(14) "AssertionError"
[2]=>
string(15) "php_user_filter"
[3]=>
string(12) "StreamBucket"
[4]=>
string(9) "Directory"
}
AssertionError
Directory
RoundingMode
StreamBucket
__PHP_Incomplete_Class
php_user_filter

View File

@@ -34,6 +34,7 @@
#include "ext/session/php_session.h"
#include "zend_exceptions.h"
#include "zend_attributes.h"
#include "zend_enum.h"
#include "zend_ini.h"
#include "zend_operators.h"
#include "ext/standard/php_dns.h"
@@ -304,6 +305,8 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
assertion_error_ce = register_class_AssertionError(zend_ce_error);
rounding_mode_ce = register_class_RoundingMode();
BASIC_MINIT_SUBMODULE(var)
BASIC_MINIT_SUBMODULE(file)
BASIC_MINIT_SUBMODULE(pack)

View File

@@ -375,26 +375,6 @@ const PHP_ROUND_HALF_EVEN = UNKNOWN;
* @cvalue PHP_ROUND_HALF_ODD
*/
const PHP_ROUND_HALF_ODD = UNKNOWN;
/**
* @var int
* @cvalue PHP_ROUND_CEILING
*/
const PHP_ROUND_CEILING = UNKNOWN;
/**
* @var int
* @cvalue PHP_ROUND_FLOOR
*/
const PHP_ROUND_FLOOR = UNKNOWN;
/**
* @var int
* @cvalue PHP_ROUND_TOWARD_ZERO
*/
const PHP_ROUND_TOWARD_ZERO = UNKNOWN;
/**
* @var int
* @cvalue PHP_ROUND_AWAY_FROM_ZERO
*/
const PHP_ROUND_AWAY_FROM_ZERO = UNKNOWN;
/* crypt.c */
@@ -3145,8 +3125,19 @@ function ceil(int|float $num): float {}
/** @compile-time-eval */
function floor(int|float $num): float {}
enum RoundingMode {
case HalfAwayFromZero;
case HalfTowardsZero;
case HalfEven;
case HalfOdd;
case TowardsZero;
case AwayFromZero;
case NegativeInfinity;
case PositiveInfinity;
}
/** @compile-time-eval */
function round(int|float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): float {}
function round(int|float $num, int $precision = 0, int|RoundingMode $mode = RoundingMode::HalfAwayFromZero): float {}
/** @compile-time-eval */
function sin(float $num): float {}

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: e3a7bf1e9f0df3e38f6280f91af70e54ce492a60 */
* Stub hash: 504f4172ac1d64535719234888400063eb37361b */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
@@ -1627,7 +1627,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_round, 0, 1, IS_DOUBLE, 0)
ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_DOUBLE, NULL)
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_ARG_OBJ_TYPE_MASK(0, mode, RoundingMode, MAY_BE_LONG, "RoundingMode::HalfAwayFromZero")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sin, 0, 1, IS_DOUBLE, 0)
@@ -3615,6 +3615,10 @@ static const zend_function_entry class_AssertionError_methods[] = {
ZEND_FE_END
};
static const zend_function_entry class_RoundingMode_methods[] = {
ZEND_FE_END
};
static void register_basic_functions_symbols(int module_number)
{
REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", PHP_EXTR_OVERWRITE, CONST_PERSISTENT);
@@ -3704,10 +3708,6 @@ static void register_basic_functions_symbols(int module_number)
REGISTER_LONG_CONSTANT("PHP_ROUND_HALF_DOWN", PHP_ROUND_HALF_DOWN, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_HALF_EVEN", PHP_ROUND_HALF_EVEN, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_HALF_ODD", PHP_ROUND_HALF_ODD, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_CEILING", PHP_ROUND_CEILING, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_FLOOR", PHP_ROUND_FLOOR, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_TOWARD_ZERO", PHP_ROUND_TOWARD_ZERO, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PHP_ROUND_AWAY_FROM_ZERO", PHP_ROUND_AWAY_FROM_ZERO, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CRYPT_SALT_LENGTH", PHP_MAX_SALT_LEN, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CRYPT_STD_DES", 1, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CRYPT_EXT_DES", 1, CONST_PERSISTENT);
@@ -4197,3 +4197,26 @@ static zend_class_entry *register_class_AssertionError(zend_class_entry *class_e
return class_entry;
}
static zend_class_entry *register_class_RoundingMode(void)
{
zend_class_entry *class_entry = zend_register_internal_enum("RoundingMode", IS_UNDEF, class_RoundingMode_methods);
zend_enum_add_case_cstr(class_entry, "HalfAwayFromZero", NULL);
zend_enum_add_case_cstr(class_entry, "HalfTowardsZero", NULL);
zend_enum_add_case_cstr(class_entry, "HalfEven", NULL);
zend_enum_add_case_cstr(class_entry, "HalfOdd", NULL);
zend_enum_add_case_cstr(class_entry, "TowardsZero", NULL);
zend_enum_add_case_cstr(class_entry, "AwayFromZero", NULL);
zend_enum_add_case_cstr(class_entry, "NegativeInfinity", NULL);
zend_enum_add_case_cstr(class_entry, "PositiveInfinity", NULL);
return class_entry;
}

View File

@@ -20,6 +20,7 @@
#include "php.h"
#include "php_math.h"
#include "zend_bitset.h"
#include "zend_enum.h"
#include "zend_exceptions.h"
#include "zend_multiply.h"
#include "zend_portability.h"
@@ -30,6 +31,8 @@
#include "basic_functions.h"
PHPAPI zend_class_entry *rounding_mode_ce;
/* {{{ php_intpow10
Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
static inline double php_intpow10(int power) {
@@ -301,6 +304,32 @@ PHP_FUNCTION(floor)
}
/* }}} */
int php_math_round_mode_from_enum(zend_object *mode)
{
zval *case_name = zend_enum_fetch_case_name(mode);
zend_string *mode_name = Z_STR_P(case_name);
switch (ZSTR_VAL(mode_name)[0] + ZSTR_VAL(mode_name)[4]) {
case 'H' + 'A':
return PHP_ROUND_HALF_UP;
case 'H' + 'T':
return PHP_ROUND_HALF_DOWN;
case 'H' + 'E':
return PHP_ROUND_HALF_EVEN;
case 'H' + 'O':
return PHP_ROUND_HALF_ODD;
case 'T' + 'r':
return PHP_ROUND_TOWARD_ZERO;
case 'A' + 'F':
return PHP_ROUND_AWAY_FROM_ZERO;
case 'N' + 't':
return PHP_ROUND_FLOOR;
case 'P' + 't':
return PHP_ROUND_CEILING;
EMPTY_SWITCH_DEFAULT_CASE();
}
}
/* {{{ Returns the number rounded to specified precision */
PHP_FUNCTION(round)
{
@@ -308,12 +337,13 @@ PHP_FUNCTION(round)
int places = 0;
zend_long precision = 0;
zend_long mode = PHP_ROUND_HALF_UP;
zend_object *mode_object = NULL;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_NUMBER(value)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(precision)
Z_PARAM_LONG(mode)
Z_PARAM_OBJ_OF_CLASS_OR_LONG(mode_object, rounding_mode_ce, mode)
ZEND_PARSE_PARAMETERS_END();
if (ZEND_NUM_ARGS() >= 2) {
@@ -324,6 +354,10 @@ PHP_FUNCTION(round)
}
}
if (mode_object != NULL) {
mode = php_math_round_mode_from_enum(mode_object);
}
switch (mode) {
case PHP_ROUND_HALF_UP:
case PHP_ROUND_HALF_DOWN:
@@ -335,7 +369,7 @@ PHP_FUNCTION(round)
case PHP_ROUND_FLOOR:
break;
default:
zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
zend_argument_value_error(3, "must be a valid rounding mode (RoundingMode::*)");
RETURN_THROWS();
}

View File

@@ -15,6 +15,8 @@
+----------------------------------------------------------------------+
*/
#include "php.h"
/* 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 */
@@ -47,3 +49,7 @@
#ifndef PHP_ROUND_AWAY_FROM_ZERO
#define PHP_ROUND_AWAY_FROM_ZERO 0x08
#endif
extern PHPAPI zend_class_entry *rounding_mode_ce;
int php_math_round_mode_from_enum(zend_object *mode);

View File

@@ -0,0 +1,102 @@
--TEST--
round(): Test RoundingMode enum.
--FILE--
<?php
foreach (RoundingMode::cases() as $mode) {
foreach ([
1.0,
-1.0,
1.2,
-1.2,
1.7,
-1.7,
1.5,
-1.5,
2.5,
-2.5
] as $number) {
printf("%-20s: %+.17g -> %+.17g\n", $mode->name, $number, round($number, 0, $mode));
}
}
?>
--EXPECT--
HalfAwayFromZero : +1 -> +1
HalfAwayFromZero : -1 -> -1
HalfAwayFromZero : +1.2 -> +1
HalfAwayFromZero : -1.2 -> -1
HalfAwayFromZero : +1.7 -> +2
HalfAwayFromZero : -1.7 -> -2
HalfAwayFromZero : +1.5 -> +2
HalfAwayFromZero : -1.5 -> -2
HalfAwayFromZero : +2.5 -> +3
HalfAwayFromZero : -2.5 -> -3
HalfTowardsZero : +1 -> +1
HalfTowardsZero : -1 -> -1
HalfTowardsZero : +1.2 -> +1
HalfTowardsZero : -1.2 -> -1
HalfTowardsZero : +1.7 -> +2
HalfTowardsZero : -1.7 -> -2
HalfTowardsZero : +1.5 -> +1
HalfTowardsZero : -1.5 -> -1
HalfTowardsZero : +2.5 -> +2
HalfTowardsZero : -2.5 -> -2
HalfEven : +1 -> +1
HalfEven : -1 -> -1
HalfEven : +1.2 -> +1
HalfEven : -1.2 -> -1
HalfEven : +1.7 -> +2
HalfEven : -1.7 -> -2
HalfEven : +1.5 -> +2
HalfEven : -1.5 -> -2
HalfEven : +2.5 -> +2
HalfEven : -2.5 -> -2
HalfOdd : +1 -> +1
HalfOdd : -1 -> -1
HalfOdd : +1.2 -> +1
HalfOdd : -1.2 -> -1
HalfOdd : +1.7 -> +2
HalfOdd : -1.7 -> -2
HalfOdd : +1.5 -> +1
HalfOdd : -1.5 -> -1
HalfOdd : +2.5 -> +3
HalfOdd : -2.5 -> -3
TowardsZero : +1 -> +1
TowardsZero : -1 -> -1
TowardsZero : +1.2 -> +1
TowardsZero : -1.2 -> -1
TowardsZero : +1.7 -> +1
TowardsZero : -1.7 -> -1
TowardsZero : +1.5 -> +1
TowardsZero : -1.5 -> -1
TowardsZero : +2.5 -> +2
TowardsZero : -2.5 -> -2
AwayFromZero : +1 -> +1
AwayFromZero : -1 -> -1
AwayFromZero : +1.2 -> +2
AwayFromZero : -1.2 -> -2
AwayFromZero : +1.7 -> +2
AwayFromZero : -1.7 -> -2
AwayFromZero : +1.5 -> +2
AwayFromZero : -1.5 -> -2
AwayFromZero : +2.5 -> +3
AwayFromZero : -2.5 -> -3
NegativeInfinity : +1 -> +1
NegativeInfinity : -1 -> -1
NegativeInfinity : +1.2 -> +1
NegativeInfinity : -1.2 -> -2
NegativeInfinity : +1.7 -> +1
NegativeInfinity : -1.7 -> -2
NegativeInfinity : +1.5 -> +1
NegativeInfinity : -1.5 -> -2
NegativeInfinity : +2.5 -> +2
NegativeInfinity : -2.5 -> -3
PositiveInfinity : +1 -> +1
PositiveInfinity : -1 -> -1
PositiveInfinity : +1.2 -> +2
PositiveInfinity : -1.2 -> -1
PositiveInfinity : +1.7 -> +2
PositiveInfinity : -1.7 -> -1
PositiveInfinity : +1.5 -> +2
PositiveInfinity : -1.5 -> -1
PositiveInfinity : +2.5 -> +3
PositiveInfinity : -2.5 -> -2

View File

@@ -38,7 +38,7 @@ $testCases = [
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
],
'PHP_ROUND_AWAY_FROM_ZERO' => [
'RoundingMode::AwayFromZero' => [
[0.12345678901234560, 16],
[-0.12345678901234560, 16],
[12345678901234567, -1],
@@ -46,7 +46,7 @@ $testCases = [
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
],
'PHP_ROUND_TOWARD_ZERO' => [
'RoundingMode::TowardsZero' => [
[0.12345678901234566, 16],
[-0.12345678901234566, 16],
[12345678901234565, -1],
@@ -54,7 +54,7 @@ $testCases = [
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
],
'PHP_ROUND_CEILING' => [
'RoundingMode::PositiveInfinity' => [
[0.12345678901234560, 16],
[-0.12345678901234560, 16],
[12345678901234564, -1],
@@ -62,7 +62,7 @@ $testCases = [
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
],
'PHP_ROUND_FLOOR' => [
'RoundingMode::NegativeInfinity' => [
[0.12345678901234560, 16],
[-0.12345678901234560, 16],
[12345678901234564, -1],
@@ -113,7 +113,7 @@ float(-12345678901234570)
float(4503599627370495)
float(-4503599627370495)
========== PHP_ROUND_AWAY_FROM_ZERO ==========
========== RoundingMode::AwayFromZero ==========
float(0.1234567890123456)
float(-0.1234567890123456)
float(12345678901234570)
@@ -121,7 +121,7 @@ float(-12345678901234570)
float(4503599627370496)
float(-4503599627370496)
========== PHP_ROUND_TOWARD_ZERO ==========
========== RoundingMode::TowardsZero ==========
float(0.1234567890123456)
float(-0.1234567890123456)
float(12345678901234560)
@@ -129,7 +129,7 @@ float(-12345678901234560)
float(4503599627370495)
float(-4503599627370495)
========== PHP_ROUND_CEILING ==========
========== RoundingMode::PositiveInfinity ==========
float(0.1234567890123456)
float(-0.1234567890123456)
float(12345678901234570)
@@ -137,7 +137,7 @@ float(-12345678901234560)
float(4503599627370496)
float(-4503599627370495)
========== PHP_ROUND_FLOOR ==========
========== RoundingMode::NegativeInfinity ==========
float(0.1234567890123456)
float(-0.1234567890123456)
float(12345678901234560)

View File

@@ -7,10 +7,10 @@ $modes = [
"PHP_ROUND_HALF_DOWN" => PHP_ROUND_HALF_DOWN,
"PHP_ROUND_HALF_EVEN" => PHP_ROUND_HALF_EVEN,
"PHP_ROUND_HALF_ODD" => PHP_ROUND_HALF_ODD,
"PHP_ROUND_CEILING" => PHP_ROUND_CEILING,
"PHP_ROUND_FLOOR" => PHP_ROUND_FLOOR,
"PHP_ROUND_TOWARD_ZERO" => PHP_ROUND_TOWARD_ZERO,
"PHP_ROUND_AWAY_FROM_ZERO" => PHP_ROUND_AWAY_FROM_ZERO,
"PositiveInfinity" => RoundingMode::PositiveInfinity,
"NegativeInfinity" => RoundingMode::NegativeInfinity,
"TowardsZero" => RoundingMode::TowardsZero,
"AwayFromZero" => RoundingMode::AwayFromZero,
];
$numbers = [
@@ -455,7 +455,7 @@ mode: PHP_ROUND_HALF_ODD
0.0001 => 0.0001
-0.0001 => -0.0001
mode: PHP_ROUND_CEILING
mode: PositiveInfinity
precision: -1
2.5 => 10
-2.5 => -0
@@ -556,7 +556,7 @@ mode: PHP_ROUND_CEILING
0.0001 => 0.0001
-0.0001 => -0.0001
mode: PHP_ROUND_FLOOR
mode: NegativeInfinity
precision: -1
2.5 => 0
-2.5 => -10
@@ -657,7 +657,7 @@ mode: PHP_ROUND_FLOOR
0.0001 => 0.0001
-0.0001 => -0.0001
mode: PHP_ROUND_TOWARD_ZERO
mode: TowardsZero
precision: -1
2.5 => 0
-2.5 => -0
@@ -758,7 +758,7 @@ mode: PHP_ROUND_TOWARD_ZERO
0.0001 => 0.0001
-0.0001 => -0.0001
mode: PHP_ROUND_AWAY_FROM_ZERO
mode: AwayFromZero
precision: -1
2.5 => 10
-2.5 => -10

View File

@@ -1,10 +1,7 @@
--TEST--
round() with modes PHP_ROUND_CEILING and PHP_ROUND_FLOOR
round() with modes PositiveInfinity and NegativeInfinity
--FILE--
<?php
$modes = [
PHP_ROUND_CEILING, PHP_ROUND_FLOOR,
];
$numbers = [
2.5,
@@ -23,20 +20,20 @@ $numbers = [
-0.0001,
];
echo "mode PHP_ROUND_CEILING\n";
echo "mode PositiveInfinity\n";
foreach($numbers as $number) {
var_dump(ceil($number) === round($number, 0, PHP_ROUND_CEILING));
var_dump(ceil($number) === round($number, 0, RoundingMode::PositiveInfinity));
}
echo "\n";
echo "mode PHP_ROUND_FLOOR\n";
echo "mode NegativeInfinity\n";
foreach($numbers as $number) {
var_dump(floor($number) === round($number, 0, PHP_ROUND_FLOOR));
var_dump(floor($number) === round($number, 0, RoundingMode::NegativeInfinity));
}
?>
--EXPECT--
mode PHP_ROUND_CEILING
mode PositiveInfinity
bool(true)
bool(true)
bool(true)
@@ -52,7 +49,7 @@ bool(true)
bool(true)
bool(true)
mode PHP_ROUND_FLOOR
mode NegativeInfinity
bool(true)
bool(true)
bool(true)

View File

@@ -7,10 +7,10 @@ $modes = [
"PHP_ROUND_HALF_DOWN" => PHP_ROUND_HALF_DOWN,
"PHP_ROUND_HALF_EVEN" => PHP_ROUND_HALF_EVEN,
"PHP_ROUND_HALF_ODD" => PHP_ROUND_HALF_ODD,
"PHP_ROUND_CEILING" => PHP_ROUND_CEILING,
"PHP_ROUND_FLOOR" => PHP_ROUND_FLOOR,
"PHP_ROUND_TOWARD_ZERO" => PHP_ROUND_TOWARD_ZERO,
"PHP_ROUND_AWAY_FROM_ZERO" => PHP_ROUND_AWAY_FROM_ZERO,
"PositiveInfinity" => RoundingMode::PositiveInfinity,
"NegativeInfinity" => RoundingMode::NegativeInfinity,
"TowardsZero" => RoundingMode::TowardsZero,
"AwayFromZero" => RoundingMode::AwayFromZero,
];
$precisions = [-1, 0, 1, 2];
@@ -129,7 +129,7 @@ mode: PHP_ROUND_HALF_ODD
0.0 => 0
-0.0 => -0
mode: PHP_ROUND_CEILING
mode: PositiveInfinity
precision: -1
0 => 0
-0 => 0
@@ -154,7 +154,7 @@ mode: PHP_ROUND_CEILING
0.0 => 0
-0.0 => -0
mode: PHP_ROUND_FLOOR
mode: NegativeInfinity
precision: -1
0 => 0
-0 => 0
@@ -179,7 +179,7 @@ mode: PHP_ROUND_FLOOR
0.0 => 0
-0.0 => -0
mode: PHP_ROUND_TOWARD_ZERO
mode: TowardsZero
precision: -1
0 => 0
-0 => 0
@@ -204,7 +204,7 @@ mode: PHP_ROUND_TOWARD_ZERO
0.0 => 0
-0.0 => -0
mode: PHP_ROUND_AWAY_FROM_ZERO
mode: AwayFromZero
precision: -1
0 => 0
-0 => 0

View File

@@ -9,4 +9,4 @@ try {
}
?>
--EXPECT--
round(): Argument #3 ($mode) must be a valid rounding mode (PHP_ROUND_*)
round(): Argument #3 ($mode) must be a valid rounding mode (RoundingMode::*)