Skip to content

Commit

Permalink
RFC: Add the RoundingMode enum (php#14833)
Browse files Browse the repository at this point in the history
  • Loading branch information
3 people authored Jul 18, 2024
1 parent 81da53f commit 5905857
Show file tree
Hide file tree
Showing 29 changed files with 401 additions and 152 deletions.
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
Expand Down
27 changes: 14 additions & 13 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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).
Expand Down
10 changes: 8 additions & 2 deletions ext/bcmath/bcmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion ext/bcmath/bcmath.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
4 changes: 2 additions & 2 deletions ext/bcmath/bcmath_arginfo.h

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

104 changes: 104 additions & 0 deletions ext/bcmath/tests/bcround_all.phpt
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions ext/bcmath/tests/bcround_away_from_zero.phpt
Original file line number Diff line number Diff line change
@@ -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 ==========
Expand Down
4 changes: 2 additions & 2 deletions ext/bcmath/tests/bcround_ceiling.phpt
Original file line number Diff line number Diff line change
@@ -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 ==========
Expand Down
50 changes: 18 additions & 32 deletions ext/bcmath/tests/bcround_early_return.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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";
}
}
?>
Expand All @@ -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
7 changes: 0 additions & 7 deletions ext/bcmath/tests/bcround_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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_*)
4 changes: 2 additions & 2 deletions ext/bcmath/tests/bcround_floor.phpt
Original file line number Diff line number Diff line change
@@ -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 ==========
Expand Down
4 changes: 2 additions & 2 deletions ext/bcmath/tests/bcround_half_down.phpt
Original file line number Diff line number Diff line change
@@ -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 ==========
Expand Down
4 changes: 2 additions & 2 deletions ext/bcmath/tests/bcround_half_even.phpt
Original file line number Diff line number Diff line change
@@ -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 ==========
Expand Down
Loading

0 comments on commit 5905857

Please sign in to comment.