Correctly round rounding mode with zero edge case (#17065)
Fixes #17064 Closes #17065
This commit is contained in:
2
NEWS
2
NEWS
@ -6,6 +6,8 @@ PHP NEWS
|
|||||||
. Fixed bug GH-17049 (Correctly compare 0 and -0). (Saki Takamachi)
|
. Fixed bug GH-17049 (Correctly compare 0 and -0). (Saki Takamachi)
|
||||||
. Fixed bug GH-17061 (Now Number::round() does not remove trailing zeros).
|
. Fixed bug GH-17061 (Now Number::round() does not remove trailing zeros).
|
||||||
(Saki Takamachi)
|
(Saki Takamachi)
|
||||||
|
. Fixed bug GH-17064 (Correctly round rounding mode with zero edge case).
|
||||||
|
(Saki Takamachi)
|
||||||
|
|
||||||
- Core:
|
- Core:
|
||||||
. Fixed bug OSS-Fuzz #382922236 (Duplicate dynamic properties in hooked object
|
. Fixed bug OSS-Fuzz #382922236 (Duplicate dynamic properties in hooked object
|
||||||
|
@ -33,10 +33,54 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
|
|||||||
* - If the fractional part ends with zeros, the zeros are omitted and the number of digits in num is reduced.
|
* - 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.
|
* Meaning we might end up in the previous case.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* e.g. value is 0.1 and precision is -3, ret is 0 or 1000 */
|
||||||
if (precision < 0 && num->n_len < (size_t) (-(precision + Z_L(1))) + 1) {
|
if (precision < 0 && num->n_len < (size_t) (-(precision + Z_L(1))) + 1) {
|
||||||
*result = bc_copy_num(BCG(_zero_));
|
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_TOWARD_ZERO:
|
||||||
|
*result = bc_copy_num(BCG(_zero_));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case PHP_ROUND_CEILING:
|
||||||
|
if (num->n_sign == MINUS) {
|
||||||
|
*result = bc_copy_num(BCG(_zero_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PHP_ROUND_FLOOR:
|
||||||
|
if (num->n_sign == PLUS) {
|
||||||
|
*result = bc_copy_num(BCG(_zero_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PHP_ROUND_AWAY_FROM_ZERO:
|
||||||
|
break;
|
||||||
|
|
||||||
|
EMPTY_SWITCH_DEFAULT_CASE()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bc_is_zero(num)) {
|
||||||
|
*result = bc_copy_num(BCG(_zero_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If precision is -3, it becomes 1000. */
|
||||||
|
if (UNEXPECTED(precision == ZEND_LONG_MIN)) {
|
||||||
|
*result = bc_new_num((size_t) ZEND_LONG_MAX + 2, 0);
|
||||||
|
} else {
|
||||||
|
*result = bc_new_num(-precision + 1, 0);
|
||||||
|
}
|
||||||
|
(*result)->n_value[0] = 1;
|
||||||
|
(*result)->n_sign = num->n_sign;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Just like bcadd('1', '1', 4) becomes '2.0000', it pads with zeros at the end if necessary. */
|
/* 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 (precision >= 0 && num->n_scale <= precision) {
|
||||||
if (num->n_scale == precision) {
|
if (num->n_scale == precision) {
|
||||||
@ -61,7 +105,7 @@ void bc_round(bc_num num, zend_long precision, zend_long mode, bc_num *result)
|
|||||||
* If the result of rounding is carried over, it will be added later, so first set it to 0 here.
|
* If the result of rounding is carried over, it will be added later, so first set it to 0 here.
|
||||||
*/
|
*/
|
||||||
if (rounded_len == 0) {
|
if (rounded_len == 0) {
|
||||||
*result = bc_copy_num(BCG(_zero_));
|
*result = bc_new_num(1, 0);
|
||||||
} else {
|
} else {
|
||||||
*result = bc_new_num(num->n_len, precision > 0 ? precision : 0);
|
*result = bc_new_num(num->n_len, precision > 0 ? precision : 0);
|
||||||
memcpy((*result)->n_value, num->n_value, rounded_len);
|
memcpy((*result)->n_value, num->n_value, rounded_len);
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::AwayFromZero);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 1000
|
||||||
|
[-0.01, -3] => -1000
|
||||||
[50, -2] => 100
|
[50, -2] => 100
|
||||||
[-50, -2] => -100
|
[-50, -2] => -100
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::PositiveInfinity);
|
|||||||
[-1.9, 0] => -1
|
[-1.9, 0] => -1
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 1000
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 100
|
[50, -2] => 100
|
||||||
[-50, -2] => 0
|
[-50, -2] => 0
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -6,15 +6,11 @@ bcmath
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$early_return_cases = [
|
$early_return_cases = [
|
||||||
['123', -4],
|
|
||||||
['123.123456', -4],
|
|
||||||
['123', 1],
|
['123', 1],
|
||||||
['123.5', 1],
|
['123.5', 1],
|
||||||
['123.5', 2],
|
['123.5', 2],
|
||||||
['123.0000000000000000000001', 22],
|
['123.0000000000000000000001', 22],
|
||||||
['123.0000000000000000000001', 23],
|
['123.0000000000000000000001', 23],
|
||||||
['-123', -4],
|
|
||||||
['-123.123456', -4],
|
|
||||||
['-123', 1],
|
['-123', 1],
|
||||||
['-123.5', 1],
|
['-123.5', 1],
|
||||||
['-123.5', 2],
|
['-123.5', 2],
|
||||||
@ -54,15 +50,11 @@ foreach (RoundingMode::cases() as $mode) {
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
[123, -4] => 0
|
|
||||||
[123.123456, -4] => 0
|
|
||||||
[123, 1] => 123.0
|
[123, 1] => 123.0
|
||||||
[123.5, 1] => 123.5
|
[123.5, 1] => 123.5
|
||||||
[123.5, 2] => 123.50
|
[123.5, 2] => 123.50
|
||||||
[123.0000000000000000000001, 22] => 123.0000000000000000000001
|
[123.0000000000000000000001, 22] => 123.0000000000000000000001
|
||||||
[123.0000000000000000000001, 23] => 123.00000000000000000000010
|
[123.0000000000000000000001, 23] => 123.00000000000000000000010
|
||||||
[-123, -4] => 0
|
|
||||||
[-123.123456, -4] => 0
|
|
||||||
[-123, 1] => -123.0
|
[-123, 1] => -123.0
|
||||||
[-123.5, 1] => -123.5
|
[-123.5, 1] => -123.5
|
||||||
[-123.5, 2] => -123.50
|
[-123.5, 2] => -123.50
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::NegativeInfinity);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => -1000
|
||||||
[50, -2] => 0
|
[50, -2] => 0
|
||||||
[-50, -2] => -100
|
[-50, -2] => -100
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::HalfTowardsZero);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 0
|
[50, -2] => 0
|
||||||
[-50, -2] => 0
|
[-50, -2] => 0
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::HalfEven);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 0
|
[50, -2] => 0
|
||||||
[-50, -2] => 0
|
[-50, -2] => 0
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::HalfOdd);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 100
|
[50, -2] => 100
|
||||||
[-50, -2] => -100
|
[-50, -2] => -100
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::HalfAwayFromZero);
|
|||||||
[-1.9, 0] => -2
|
[-1.9, 0] => -2
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 100
|
[50, -2] => 100
|
||||||
[-50, -2] => -100
|
[-50, -2] => -100
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -30,6 +30,9 @@ function run_round_test(RoundingMode $mode)
|
|||||||
];
|
];
|
||||||
|
|
||||||
$minus_precision_cases = [
|
$minus_precision_cases = [
|
||||||
|
['0', -3],
|
||||||
|
['0.01', -3],
|
||||||
|
['-0.01', -3],
|
||||||
['50', -2],
|
['50', -2],
|
||||||
['-50', -2],
|
['-50', -2],
|
||||||
['1230', -1],
|
['1230', -1],
|
||||||
|
@ -27,6 +27,9 @@ run_round_test(RoundingMode::TowardsZero);
|
|||||||
[-1.9, 0] => -1
|
[-1.9, 0] => -1
|
||||||
|
|
||||||
========== minus precision ==========
|
========== minus precision ==========
|
||||||
|
[0, -3] => 0
|
||||||
|
[0.01, -3] => 0
|
||||||
|
[-0.01, -3] => 0
|
||||||
[50, -2] => 0
|
[50, -2] => 0
|
||||||
[-50, -2] => 0
|
[-50, -2] => 0
|
||||||
[1230, -1] => 1230
|
[1230, -1] => 1230
|
||||||
|
@ -6,6 +6,7 @@ bcmath
|
|||||||
<?php
|
<?php
|
||||||
foreach (RoundingMode::cases() as $mode) {
|
foreach (RoundingMode::cases() as $mode) {
|
||||||
foreach ([
|
foreach ([
|
||||||
|
'0',
|
||||||
'0.1',
|
'0.1',
|
||||||
'-0.1',
|
'-0.1',
|
||||||
'1.0',
|
'1.0',
|
||||||
@ -19,17 +20,20 @@ foreach (RoundingMode::cases() as $mode) {
|
|||||||
'2.5',
|
'2.5',
|
||||||
'-2.5',
|
'-2.5',
|
||||||
] as $number) {
|
] as $number) {
|
||||||
$func_ret = bcround($number, 0, $mode);
|
foreach ([0, 5, -5] as $scale) {
|
||||||
$method_ret = (new BcMath\Number($number))->round(0, $mode);
|
$func_ret = bcround($number, $scale, $mode);
|
||||||
if ($method_ret->compare($func_ret) !== 0) {
|
$method_ret = (new BcMath\Number($number))->round($scale, $mode);
|
||||||
echo "Result is incorrect.\n";
|
if ($method_ret->compare($func_ret) !== 0) {
|
||||||
var_dump($number, $mode, $func_ret, $method_ret);
|
echo "Result is incorrect.\n";
|
||||||
|
var_dump($number, $mode, $func_ret, $method_ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (RoundingMode::cases() as $mode) {
|
foreach (RoundingMode::cases() as $mode) {
|
||||||
foreach ([
|
foreach ([
|
||||||
|
'0',
|
||||||
'1.2345678',
|
'1.2345678',
|
||||||
'-1.2345678',
|
'-1.2345678',
|
||||||
] as $number) {
|
] as $number) {
|
||||||
|
Reference in New Issue
Block a user