Skip to content

Commit bf97589

Browse files
committed
Added edge case generation logic for cases where the precision is so fine that adding 0.5 does not change the value.
1 parent 86e5c67 commit bf97589

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

‎ext/standard/math.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,21 @@ static inline double php_intpow10(int power) {
5151

5252
static zend_always_inline double php_round_get_basic_edge_case(double integral, double exponent, int places)
5353
{
54-
return (places > 0)
55-
? fabs((integral + copysign(0.5, integral)) / exponent)
56-
: fabs((integral + copysign(0.5, integral)) * exponent);
54+
/* Integer + `0.5` can be represented exactly in a double, so in most cases, there is no rounding error at this point. */
55+
double edge_case = integral + copysign(0.5, integral);
56+
57+
/* When the calculation precision is as high as `1e16`, adding `0.5` may be lost due to rounding.
58+
* In such cases, the exponent is applied individually before performing the addition.
59+
* This approach is not used all the time in order to avoid introducing rounding errors when adding `0.5`.
60+
* It is only used when the precision is so fine that adding `0.5` would have no meaningful effect. */
61+
if (UNEXPECTED(integral == edge_case)) {
62+
return (places > 0)
63+
? fabs((integral / exponent) + (copysign(0.5, integral) / exponent))
64+
: fabs((integral * exponent) + (copysign(0.5, integral) * exponent));
65+
}
66+
67+
/* Returns a value adjusted with the exponent so that it can be compared with the input. */
68+
return (places > 0) ? fabs(edge_case / exponent) : fabs(edge_case * exponent);
5769
}
5870

5971
static zend_always_inline double php_round_get_zero_edge_case(double integral, double exponent, int places)

‎ext/standard/tests/math/round_gh12143_expand_rounding_target.phpt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ $testCases = [
1313
[-12345678901234565, -1],
1414
[4503599627370495.5, 0],
1515
[-4503599627370495.5, 0],
16+
[5.0, 15],
17+
[5000000000000000.0, 0],
18+
[5000000000000000.5, 0],
1619
],
1720
'PHP_ROUND_HALF_DOWN' => [
1821
[0.12345678901234565, 16],
@@ -21,6 +24,9 @@ $testCases = [
2124
[-12345678901234565, -1],
2225
[4503599627370495.5, 0],
2326
[-4503599627370495.5, 0],
27+
[5.0, 15],
28+
[5000000000000000.0, 0],
29+
[5000000000000000.5, 0],
2430
],
2531
'PHP_ROUND_HALF_EVEN' => [
2632
[0.12345678901234565, 16],
@@ -29,6 +35,9 @@ $testCases = [
2935
[-12345678901234565, -1],
3036
[4503599627370495.5, 0],
3137
[-4503599627370495.5, 0],
38+
[5.0, 15],
39+
[5000000000000000.0, 0],
40+
[5000000000000000.5, 0],
3241
],
3342
'PHP_ROUND_HALF_ODD' => [
3443
[0.12345678901234565, 16],
@@ -37,6 +46,9 @@ $testCases = [
3746
[-12345678901234565, -1],
3847
[4503599627370495.5, 0],
3948
[-4503599627370495.5, 0],
49+
[5.0, 15],
50+
[5000000000000000.0, 0],
51+
[5000000000000000.5, 0],
4052
],
4153
'RoundingMode::AwayFromZero' => [
4254
[0.12345678901234560, 16],
@@ -45,6 +57,9 @@ $testCases = [
4557
[-12345678901234567, -1],
4658
[4503599627370495.5, 0],
4759
[-4503599627370495.5, 0],
60+
[5.0, 15],
61+
[5000000000000000.0, 0],
62+
[5000000000000000.5, 0],
4863
],
4964
'RoundingMode::TowardsZero' => [
5065
[0.12345678901234566, 16],
@@ -53,6 +68,9 @@ $testCases = [
5368
[-12345678901234565, -1],
5469
[4503599627370495.5, 0],
5570
[-4503599627370495.5, 0],
71+
[5.0, 15],
72+
[5000000000000000.0, 0],
73+
[5000000000000000.5, 0],
5674
],
5775
'RoundingMode::PositiveInfinity' => [
5876
[0.12345678901234560, 16],
@@ -61,6 +79,9 @@ $testCases = [
6179
[-12345678901234564, -1],
6280
[4503599627370495.5, 0],
6381
[-4503599627370495.5, 0],
82+
[5.0, 15],
83+
[5000000000000000.0, 0],
84+
[5000000000000000.5, 0],
6485
],
6586
'RoundingMode::NegativeInfinity' => [
6687
[0.12345678901234560, 16],
@@ -69,6 +90,9 @@ $testCases = [
6990
[-12345678901234564, -1],
7091
[4503599627370495.5, 0],
7192
[-4503599627370495.5, 0],
93+
[5.0, 15],
94+
[5000000000000000.0, 0],
95+
[5000000000000000.5, 0],
7296
],
7397
];
7498

@@ -88,6 +112,9 @@ float(12345678901234570)
88112
float(-12345678901234570)
89113
float(4503599627370496)
90114
float(-4503599627370496)
115+
float(5)
116+
float(5000000000000001)
117+
float(5000000000000001)
91118

92119
========== PHP_ROUND_HALF_DOWN ==========
93120
float(0.1234567890123456)
@@ -96,6 +123,9 @@ float(12345678901234560)
96123
float(-12345678901234560)
97124
float(4503599627370495)
98125
float(-4503599627370495)
126+
float(5)
127+
float(5000000000000000)
128+
float(5000000000000000)
99129

100130
========== PHP_ROUND_HALF_EVEN ==========
101131
float(0.1234567890123456)
@@ -104,6 +134,9 @@ float(12345678901234560)
104134
float(-12345678901234560)
105135
float(4503599627370496)
106136
float(-4503599627370496)
137+
float(5)
138+
float(5000000000000000)
139+
float(5000000000000000)
107140

108141
========== PHP_ROUND_HALF_ODD ==========
109142
float(0.1234567890123457)
@@ -112,6 +145,9 @@ float(12345678901234570)
112145
float(-12345678901234570)
113146
float(4503599627370495)
114147
float(-4503599627370495)
148+
float(5)
149+
float(5000000000000001)
150+
float(5000000000000001)
115151

116152
========== RoundingMode::AwayFromZero ==========
117153
float(0.1234567890123456)
@@ -120,6 +156,9 @@ float(12345678901234570)
120156
float(-12345678901234570)
121157
float(4503599627370496)
122158
float(-4503599627370496)
159+
float(5)
160+
float(5000000000000000)
161+
float(5000000000000000)
123162

124163
========== RoundingMode::TowardsZero ==========
125164
float(0.1234567890123456)
@@ -128,6 +167,9 @@ float(12345678901234560)
128167
float(-12345678901234560)
129168
float(4503599627370495)
130169
float(-4503599627370495)
170+
float(5)
171+
float(5000000000000000)
172+
float(5000000000000000)
131173

132174
========== RoundingMode::PositiveInfinity ==========
133175
float(0.1234567890123456)
@@ -136,6 +178,9 @@ float(12345678901234570)
136178
float(-12345678901234560)
137179
float(4503599627370496)
138180
float(-4503599627370495)
181+
float(5)
182+
float(5000000000000000)
183+
float(5000000000000000)
139184

140185
========== RoundingMode::NegativeInfinity ==========
141186
float(0.1234567890123456)
@@ -144,3 +189,6 @@ float(12345678901234560)
144189
float(-12345678901234570)
145190
float(4503599627370495)
146191
float(-4503599627370496)
192+
float(5)
193+
float(5000000000000000)
194+
float(5000000000000000)

0 commit comments

Comments
 (0)