Skip to content

Fixed GH-18266: round() - Added edge case generation logic #18936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: PHP-8.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions ext/standard/math.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,21 @@ static inline double php_intpow10(int power) {

static zend_always_inline double php_round_get_basic_edge_case(double integral, double exponent, int places)
{
return (places > 0)
? fabs((integral + copysign(0.5, integral)) / exponent)
: fabs((integral + copysign(0.5, integral)) * exponent);
/* Integer + `0.5` can be represented exactly in a double, so in most cases, there is no rounding error at this point. */
double edge_case = integral + copysign(0.5, integral);

/* When the calculation precision is as high as `1e16`, adding `0.5` may be lost due to rounding.
* In such cases, the exponent is applied individually before performing the addition.
* This approach is not used all the time in order to avoid introducing rounding errors when adding `0.5`.
* It is only used when the precision is so fine that adding `0.5` would have no meaningful effect. */
if (UNEXPECTED(integral == edge_case)) {
return (places > 0)
? fabs((integral / exponent) + (copysign(0.5, integral) / exponent))
: fabs((integral * exponent) + (copysign(0.5, integral) * exponent));
}

/* Returns a value adjusted with the exponent so that it can be compared with the input. */
return (places > 0) ? fabs(edge_case / exponent) : fabs(edge_case * exponent);
}

static zend_always_inline double php_round_get_zero_edge_case(double integral, double exponent, int places)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ $testCases = [
[-12345678901234565, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'PHP_ROUND_HALF_DOWN' => [
[0.12345678901234565, 16],
Expand All @@ -21,6 +24,9 @@ $testCases = [
[-12345678901234565, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'PHP_ROUND_HALF_EVEN' => [
[0.12345678901234565, 16],
Expand All @@ -29,6 +35,9 @@ $testCases = [
[-12345678901234565, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'PHP_ROUND_HALF_ODD' => [
[0.12345678901234565, 16],
Expand All @@ -37,6 +46,9 @@ $testCases = [
[-12345678901234565, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'RoundingMode::AwayFromZero' => [
[0.12345678901234560, 16],
Expand All @@ -45,6 +57,9 @@ $testCases = [
[-12345678901234567, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'RoundingMode::TowardsZero' => [
[0.12345678901234566, 16],
Expand All @@ -53,6 +68,9 @@ $testCases = [
[-12345678901234565, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'RoundingMode::PositiveInfinity' => [
[0.12345678901234560, 16],
Expand All @@ -61,6 +79,9 @@ $testCases = [
[-12345678901234564, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
'RoundingMode::NegativeInfinity' => [
[0.12345678901234560, 16],
Expand All @@ -69,6 +90,9 @@ $testCases = [
[-12345678901234564, -1],
[4503599627370495.5, 0],
[-4503599627370495.5, 0],
[5.0, 15],
[5000000000000000.0, 0],
[5000000000000000.5, 0],
],
];

Expand All @@ -88,6 +112,9 @@ float(12345678901234570)
float(-12345678901234570)
float(4503599627370496)
float(-4503599627370496)
float(5)
float(5000000000000001)
float(5000000000000001)

========== PHP_ROUND_HALF_DOWN ==========
float(0.1234567890123456)
Expand All @@ -96,6 +123,9 @@ float(12345678901234560)
float(-12345678901234560)
float(4503599627370495)
float(-4503599627370495)
float(5)
float(5000000000000000)
float(5000000000000000)

========== PHP_ROUND_HALF_EVEN ==========
float(0.1234567890123456)
Expand All @@ -104,6 +134,9 @@ float(12345678901234560)
float(-12345678901234560)
float(4503599627370496)
float(-4503599627370496)
float(5)
float(5000000000000000)
float(5000000000000000)

========== PHP_ROUND_HALF_ODD ==========
float(0.1234567890123457)
Expand All @@ -112,6 +145,9 @@ float(12345678901234570)
float(-12345678901234570)
float(4503599627370495)
float(-4503599627370495)
float(5)
float(5000000000000001)
float(5000000000000001)

========== RoundingMode::AwayFromZero ==========
float(0.1234567890123456)
Expand All @@ -120,6 +156,9 @@ float(12345678901234570)
float(-12345678901234570)
float(4503599627370496)
float(-4503599627370496)
float(5)
float(5000000000000000)
float(5000000000000000)

========== RoundingMode::TowardsZero ==========
float(0.1234567890123456)
Expand All @@ -128,6 +167,9 @@ float(12345678901234560)
float(-12345678901234560)
float(4503599627370495)
float(-4503599627370495)
float(5)
float(5000000000000000)
float(5000000000000000)

========== RoundingMode::PositiveInfinity ==========
float(0.1234567890123456)
Expand All @@ -136,6 +178,9 @@ float(12345678901234570)
float(-12345678901234560)
float(4503599627370496)
float(-4503599627370495)
float(5)
float(5000000000000000)
float(5000000000000000)

========== RoundingMode::NegativeInfinity ==========
float(0.1234567890123456)
Expand All @@ -144,3 +189,6 @@ float(12345678901234560)
float(-12345678901234570)
float(4503599627370495)
float(-4503599627370496)
float(5)
float(5000000000000000)
float(5000000000000000)