Skip to content

Commit 8d33f2b

Browse files
committed
Correctly round rounding mode with zero edge case
1 parent 3e2cfdf commit 8d33f2b

13 files changed

+66
-15
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.4.3
44

5+
- BcMath:
6+
. Fixed bug GH-17064 (Correctly round rounding mode with zero edge case).
7+
(Saki Takamachi)
8+
59
- Streams:
610
. Fixed bug GH-17037 (UAF in user filter when adding existing filter name due
711
to incorrect error handling). (nielsdos)

ext/bcmath/libbcmath/src/round.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,45 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
3333
* - If the fractional part ends with zeros, the zeros are omitted and the number of digits in num is reduced.
3434
* Meaning we might end up in the previous case.
3535
*/
36+
37+
/* e.g. value is 0.1 and precision is -3, ret is 0 or 1000 */
3638
if (precision < 0 && num->n_len < (size_t) (-(precision + Z_L(1))) + 1) {
37-
*result = bc_copy_num(BCG(_zero_));
39+
switch (mode) {
40+
case PHP_ROUND_HALF_UP:
41+
case PHP_ROUND_HALF_DOWN:
42+
case PHP_ROUND_HALF_EVEN:
43+
case PHP_ROUND_HALF_ODD:
44+
case PHP_ROUND_TOWARD_ZERO:
45+
*result = bc_copy_num(BCG(_zero_));
46+
return;
47+
48+
case PHP_ROUND_CEILING:
49+
if (num->n_sign == MINUS) {
50+
*result = bc_copy_num(BCG(_zero_));
51+
return;
52+
}
53+
break;
54+
55+
case PHP_ROUND_FLOOR:
56+
if (num->n_sign == PLUS) {
57+
*result = bc_copy_num(BCG(_zero_));
58+
return;
59+
}
60+
break;
61+
62+
case PHP_ROUND_AWAY_FROM_ZERO:
63+
break;
64+
65+
EMPTY_SWITCH_DEFAULT_CASE()
66+
}
67+
68+
/* If precision is -3, it becomes 1000. */
69+
*result = bc_new_num(-precision + 1, 0);
70+
(*result)->n_value[0] = 1;
71+
(*result)->n_sign = num->n_sign;
3872
return;
3973
}
74+
4075
/* Just like bcadd('1', '1', 4) becomes '2.0000', it pads with zeros at the end if necessary. */
4176
if (precision >= 0 && num->n_scale <= precision) {
4277
if (num->n_scale == precision) {
@@ -61,7 +96,7 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
6196
* If the result of rounding is carried over, it will be added later, so first set it to 0 here.
6297
*/
6398
if (rounded_len == 0) {
64-
*result = bc_copy_num(BCG(_zero_));
99+
*result = bc_new_num(1, 0);
65100
} else {
66101
*result = bc_new_num(num->n_len, precision > 0 ? precision : 0);
67102
memcpy((*result)->n_value, num->n_value, rounded_len);

ext/bcmath/tests/bcround_away_from_zero.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::AwayFromZero);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 1000
31+
[-0.01, -3] => -1000
3032
[50, -2] => 100
3133
[-50, -2] => -100
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_ceiling.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::PositiveInfinity);
2727
[-1.9, 0] => -1
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 1000
31+
[-0.01, -3] => 0
3032
[50, -2] => 100
3133
[-50, -2] => 0
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_early_return.phpt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,11 @@ bcmath
66
<?php
77

88
$early_return_cases = [
9-
['123', -4],
10-
['123.123456', -4],
119
['123', 1],
1210
['123.5', 1],
1311
['123.5', 2],
1412
['123.0000000000000000000001', 22],
1513
['123.0000000000000000000001', 23],
16-
['-123', -4],
17-
['-123.123456', -4],
1814
['-123', 1],
1915
['-123.5', 1],
2016
['-123.5', 2],
@@ -54,15 +50,11 @@ foreach (RoundingMode::cases() as $mode) {
5450
}
5551
?>
5652
--EXPECT--
57-
[123, -4] => 0
58-
[123.123456, -4] => 0
5953
[123, 1] => 123.0
6054
[123.5, 1] => 123.5
6155
[123.5, 2] => 123.50
6256
[123.0000000000000000000001, 22] => 123.0000000000000000000001
6357
[123.0000000000000000000001, 23] => 123.00000000000000000000010
64-
[-123, -4] => 0
65-
[-123.123456, -4] => 0
6658
[-123, 1] => -123.0
6759
[-123.5, 1] => -123.5
6860
[-123.5, 2] => -123.50

ext/bcmath/tests/bcround_floor.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::NegativeInfinity);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => -1000
3032
[50, -2] => 0
3133
[-50, -2] => -100
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_half_down.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::HalfTowardsZero);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => 0
3032
[50, -2] => 0
3133
[-50, -2] => 0
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_half_even.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::HalfEven);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => 0
3032
[50, -2] => 0
3133
[-50, -2] => 0
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_half_odd.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::HalfOdd);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => 0
3032
[50, -2] => 100
3133
[-50, -2] => -100
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_half_up.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::HalfAwayFromZero);
2727
[-1.9, 0] => -2
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => 0
3032
[50, -2] => 100
3133
[-50, -2] => -100
3234
[1230, -1] => 1230

ext/bcmath/tests/bcround_test_helper.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ function run_round_test(RoundingMode $mode)
3030
];
3131

3232
$minus_precision_cases = [
33+
['0.01', -3],
34+
['-0.01', -3],
3335
['50', -2],
3436
['-50', -2],
3537
['1230', -1],

ext/bcmath/tests/bcround_toward_zero.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ run_round_test(RoundingMode::TowardsZero);
2727
[-1.9, 0] => -1
2828

2929
========== minus precision ==========
30+
[0.01, -3] => 0
31+
[-0.01, -3] => 0
3032
[50, -2] => 0
3133
[-50, -2] => 0
3234
[1230, -1] => 1230

ext/bcmath/tests/number/methods/round.phpt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ foreach (RoundingMode::cases() as $mode) {
1919
'2.5',
2020
'-2.5',
2121
] as $number) {
22-
$func_ret = bcround($number, 0, $mode);
23-
$method_ret = (new BcMath\Number($number))->round(0, $mode);
24-
if ($method_ret->compare($func_ret) !== 0) {
25-
echo "Result is incorrect.\n";
26-
var_dump($number, $mode, $func_ret, $method_ret);
22+
foreach ([0, 5, -5] as $scale) {
23+
$func_ret = bcround($number, $scale, $mode);
24+
$method_ret = (new BcMath\Number($number))->round($scale, $mode);
25+
if ($method_ret->compare($func_ret) !== 0) {
26+
echo "Result is incorrect.\n";
27+
var_dump($number, $mode, $func_ret, $method_ret);
28+
}
2729
}
2830
}
2931
}

0 commit comments

Comments
 (0)