Skip to content

Commit 1caf057

Browse files
committed
standard: Add the RoundingMode enum for round()
1 parent fbc3297 commit 1caf057

File tree

7 files changed

+193
-6
lines changed

7 files changed

+193
-6
lines changed

ext/standard/basic_functions.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "ext/session/php_session.h"
3535
#include "zend_exceptions.h"
3636
#include "zend_attributes.h"
37+
#include "zend_enum.h"
3738
#include "zend_ini.h"
3839
#include "zend_operators.h"
3940
#include "ext/standard/php_dns.h"
@@ -304,6 +305,8 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
304305

305306
assertion_error_ce = register_class_AssertionError(zend_ce_error);
306307

308+
rounding_mode_ce = register_class_RoundingMode();
309+
307310
BASIC_MINIT_SUBMODULE(var)
308311
BASIC_MINIT_SUBMODULE(file)
309312
BASIC_MINIT_SUBMODULE(pack)

ext/standard/basic_functions.stub.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3145,8 +3145,19 @@ function ceil(int|float $num): float {}
31453145
/** @compile-time-eval */
31463146
function floor(int|float $num): float {}
31473147

3148+
enum RoundingMode {
3149+
case HalfAwayFromZero;
3150+
case HalfTowardsZero;
3151+
case HalfEven;
3152+
case HalfOdd;
3153+
case TowardsZero;
3154+
case AwayFromZero;
3155+
case NegativeInfinity;
3156+
case PositiveInfinity;
3157+
}
3158+
31483159
/** @compile-time-eval */
3149-
function round(int|float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): float {}
3160+
function round(int|float $num, int $precision = 0, int|RoundingMode $mode = RoundingMode::HalfAwayFromZero): float {}
31503161

31513162
/** @compile-time-eval */
31523163
function sin(float $num): float {}

ext/standard/basic_functions_arginfo.h

Lines changed: 29 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/math.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "php.h"
2121
#include "php_math.h"
2222
#include "zend_bitset.h"
23+
#include "zend_enum.h"
2324
#include "zend_exceptions.h"
2425
#include "zend_multiply.h"
2526
#include "zend_portability.h"
@@ -30,6 +31,8 @@
3031

3132
#include "basic_functions.h"
3233

34+
PHPAPI zend_class_entry *rounding_mode_ce;
35+
3336
/* {{{ php_intpow10
3437
Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
3538
static inline double php_intpow10(int power) {
@@ -301,19 +304,46 @@ PHP_FUNCTION(floor)
301304
}
302305
/* }}} */
303306

307+
int php_math_round_mode_from_enum(zend_object *mode)
308+
{
309+
zval *case_name = zend_enum_fetch_case_name(mode);
310+
zend_string *mode_name = Z_STR_P(case_name);
311+
312+
switch (ZSTR_VAL(mode_name)[0] + ZSTR_VAL(mode_name)[4]) {
313+
case 'H' + 'A':
314+
return PHP_ROUND_HALF_UP;
315+
case 'H' + 'T':
316+
return PHP_ROUND_HALF_DOWN;
317+
case 'H' + 'E':
318+
return PHP_ROUND_HALF_EVEN;
319+
case 'H' + 'O':
320+
return PHP_ROUND_HALF_ODD;
321+
case 'T' + 'r':
322+
return PHP_ROUND_TOWARD_ZERO;
323+
case 'A' + 'F':
324+
return PHP_ROUND_AWAY_FROM_ZERO;
325+
case 'N' + 't':
326+
return PHP_ROUND_FLOOR;
327+
case 'P' + 't':
328+
return PHP_ROUND_CEILING;
329+
EMPTY_SWITCH_DEFAULT_CASE();
330+
}
331+
}
332+
304333
/* {{{ Returns the number rounded to specified precision */
305334
PHP_FUNCTION(round)
306335
{
307336
zval *value;
308337
int places = 0;
309338
zend_long precision = 0;
310339
zend_long mode = PHP_ROUND_HALF_UP;
340+
zend_object *mode_object = NULL;
311341

312342
ZEND_PARSE_PARAMETERS_START(1, 3)
313343
Z_PARAM_NUMBER(value)
314344
Z_PARAM_OPTIONAL
315345
Z_PARAM_LONG(precision)
316-
Z_PARAM_LONG(mode)
346+
Z_PARAM_OBJ_OF_CLASS_OR_LONG(mode_object, rounding_mode_ce, mode)
317347
ZEND_PARSE_PARAMETERS_END();
318348

319349
if (ZEND_NUM_ARGS() >= 2) {
@@ -324,6 +354,10 @@ PHP_FUNCTION(round)
324354
}
325355
}
326356

357+
if (mode_object != NULL) {
358+
mode = php_math_round_mode_from_enum(mode_object);
359+
}
360+
327361
switch (mode) {
328362
case PHP_ROUND_HALF_UP:
329363
case PHP_ROUND_HALF_DOWN:
@@ -335,7 +369,7 @@ PHP_FUNCTION(round)
335369
case PHP_ROUND_FLOOR:
336370
break;
337371
default:
338-
zend_argument_value_error(3, "must be a valid rounding mode (PHP_ROUND_*)");
372+
zend_argument_value_error(3, "must be a valid rounding mode (RoundingMode::*)");
339373
RETURN_THROWS();
340374
}
341375

ext/standard/php_math_round_mode.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@
4747
#ifndef PHP_ROUND_AWAY_FROM_ZERO
4848
#define PHP_ROUND_AWAY_FROM_ZERO 0x08
4949
#endif
50+
51+
extern PHPAPI zend_class_entry *rounding_mode_ce;
52+
53+
int php_math_round_mode_from_enum(zend_object *mode);
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
--TEST--
2+
round(): Test RoundingMode enum.
3+
--FILE--
4+
<?php
5+
foreach (RoundingMode::cases() as $mode) {
6+
foreach ([
7+
1.0,
8+
-1.0,
9+
1.2,
10+
-1.2,
11+
1.7,
12+
-1.7,
13+
1.5,
14+
-1.5,
15+
2.5,
16+
-2.5
17+
] as $number) {
18+
printf("%-20s: %+.17g -> %+.17g\n", $mode->name, $number, round($number, 0, $mode));
19+
}
20+
}
21+
?>
22+
--EXPECT--
23+
HalfAwayFromZero : +1 -> +1
24+
HalfAwayFromZero : -1 -> -1
25+
HalfAwayFromZero : +1.2 -> +1
26+
HalfAwayFromZero : -1.2 -> -1
27+
HalfAwayFromZero : +1.7 -> +2
28+
HalfAwayFromZero : -1.7 -> -2
29+
HalfAwayFromZero : +1.5 -> +2
30+
HalfAwayFromZero : -1.5 -> -2
31+
HalfAwayFromZero : +2.5 -> +3
32+
HalfAwayFromZero : -2.5 -> -3
33+
HalfTowardsZero : +1 -> +1
34+
HalfTowardsZero : -1 -> -1
35+
HalfTowardsZero : +1.2 -> +1
36+
HalfTowardsZero : -1.2 -> -1
37+
HalfTowardsZero : +1.7 -> +2
38+
HalfTowardsZero : -1.7 -> -2
39+
HalfTowardsZero : +1.5 -> +1
40+
HalfTowardsZero : -1.5 -> -1
41+
HalfTowardsZero : +2.5 -> +2
42+
HalfTowardsZero : -2.5 -> -2
43+
HalfEven : +1 -> +1
44+
HalfEven : -1 -> -1
45+
HalfEven : +1.2 -> +1
46+
HalfEven : -1.2 -> -1
47+
HalfEven : +1.7 -> +2
48+
HalfEven : -1.7 -> -2
49+
HalfEven : +1.5 -> +2
50+
HalfEven : -1.5 -> -2
51+
HalfEven : +2.5 -> +2
52+
HalfEven : -2.5 -> -2
53+
HalfOdd : +1 -> +1
54+
HalfOdd : -1 -> -1
55+
HalfOdd : +1.2 -> +1
56+
HalfOdd : -1.2 -> -1
57+
HalfOdd : +1.7 -> +2
58+
HalfOdd : -1.7 -> -2
59+
HalfOdd : +1.5 -> +1
60+
HalfOdd : -1.5 -> -1
61+
HalfOdd : +2.5 -> +3
62+
HalfOdd : -2.5 -> -3
63+
TowardsZero : +1 -> +1
64+
TowardsZero : -1 -> -1
65+
TowardsZero : +1.2 -> +1
66+
TowardsZero : -1.2 -> -1
67+
TowardsZero : +1.7 -> +1
68+
TowardsZero : -1.7 -> -1
69+
TowardsZero : +1.5 -> +1
70+
TowardsZero : -1.5 -> -1
71+
TowardsZero : +2.5 -> +2
72+
TowardsZero : -2.5 -> -2
73+
AwayFromZero : +1 -> +1
74+
AwayFromZero : -1 -> -1
75+
AwayFromZero : +1.2 -> +2
76+
AwayFromZero : -1.2 -> -2
77+
AwayFromZero : +1.7 -> +2
78+
AwayFromZero : -1.7 -> -2
79+
AwayFromZero : +1.5 -> +2
80+
AwayFromZero : -1.5 -> -2
81+
AwayFromZero : +2.5 -> +3
82+
AwayFromZero : -2.5 -> -3
83+
NegativeInfinity : +1 -> +1
84+
NegativeInfinity : -1 -> -1
85+
NegativeInfinity : +1.2 -> +1
86+
NegativeInfinity : -1.2 -> -2
87+
NegativeInfinity : +1.7 -> +1
88+
NegativeInfinity : -1.7 -> -2
89+
NegativeInfinity : +1.5 -> +1
90+
NegativeInfinity : -1.5 -> -2
91+
NegativeInfinity : +2.5 -> +2
92+
NegativeInfinity : -2.5 -> -3
93+
PositiveInfinity : +1 -> +1
94+
PositiveInfinity : -1 -> -1
95+
PositiveInfinity : +1.2 -> +2
96+
PositiveInfinity : -1.2 -> -1
97+
PositiveInfinity : +1.7 -> +2
98+
PositiveInfinity : -1.7 -> -1
99+
PositiveInfinity : +1.5 -> +2
100+
PositiveInfinity : -1.5 -> -1
101+
PositiveInfinity : +2.5 -> +3
102+
PositiveInfinity : -2.5 -> -2

ext/standard/tests/math/round_valid_rounding_mode.phpt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ try {
77
} catch (ValueError $e) {
88
echo $e->getMessage(), PHP_EOL;
99
}
10+
try {
11+
var_dump(round(1.5, mode: Random\IntervalBoundary::ClosedOpen));
12+
} catch (TypeError $e) {
13+
echo $e->getMessage(), PHP_EOL;
14+
}
1015
?>
1116
--EXPECT--
12-
round(): Argument #3 ($mode) must be a valid rounding mode (PHP_ROUND_*)
17+
round(): Argument #3 ($mode) must be a valid rounding mode (RoundingMode::*)
18+
round(): Argument #3 ($mode) must be of type RoundingMode|int, Random\IntervalBoundary given

0 commit comments

Comments
 (0)