30
30
31
31
#include "basic_functions.h"
32
32
33
- /* {{{ php_intlog10abs
34
- Returns floor(log10(fabs(val))), uses fast binary search */
35
- static inline int php_intlog10abs (double value ) {
36
- value = fabs (value );
37
-
38
- if (value < 1e-8 || value > 1e22 ) {
39
- return (int )floor (log10 (value ));
40
- } else {
41
- /* Do a binary search with 5 steps */
42
- int result = 15 ;
43
- static const double values [] = {
44
- 1e-8 , 1e-7 , 1e-6 , 1e-5 , 1e-4 , 1e-3 , 1e-2 , 1e-1 , 1e0 , 1e1 , 1e2 ,
45
- 1e3 , 1e4 , 1e5 , 1e6 , 1e7 , 1e8 , 1e9 , 1e10 , 1e11 , 1e12 , 1e13 ,
46
- 1e14 , 1e15 , 1e16 , 1e17 , 1e18 , 1e19 , 1e20 , 1e21 , 1e22 };
47
-
48
- if (value < values [result ]) {
49
- result -= 8 ;
50
- } else {
51
- result += 8 ;
52
- }
53
- if (value < values [result ]) {
54
- result -= 4 ;
55
- } else {
56
- result += 4 ;
57
- }
58
- if (value < values [result ]) {
59
- result -= 2 ;
60
- } else {
61
- result += 2 ;
62
- }
63
- if (value < values [result ]) {
64
- result -= 1 ;
65
- } else {
66
- result += 1 ;
67
- }
68
- if (value < values [result ]) {
69
- result -= 1 ;
70
- }
71
- result -= 8 ;
72
- return result ;
73
- }
74
- }
75
- /* }}} */
76
-
77
33
/* {{{ php_intpow10
78
34
Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
79
35
static inline double php_intpow10 (int power ) {
@@ -92,20 +48,26 @@ static inline double php_intpow10(int power) {
92
48
93
49
/* {{{ php_round_helper
94
50
Actually performs the rounding of a value to integer in a certain mode */
95
- static inline double php_round_helper (double value , int mode ) {
96
- double integral , fractional ;
51
+ static inline double php_round_helper (double adjusted_value , double value , double coefficient , int mode ) {
52
+ double integral , fractional , edge_case ;
53
+ double value_abs = fabs (value );
97
54
98
55
/* Split the input value into the integral and fractional part.
99
56
*
100
57
* Both parts will have the same sign as the input value. We take
101
58
* the absolute value of the fractional part (which will not result
102
59
* in branches in the assembly) to make the following cases simpler.
103
60
*/
104
- fractional = fabs (modf (value , & integral ));
61
+ fractional = fabs (modf (adjusted_value , & integral ));
62
+ if (fabs (adjusted_value ) >= value_abs ) {
63
+ edge_case = fabs ((integral + copysign (0.5 , integral )) / coefficient );
64
+ } else {
65
+ edge_case = fabs ((integral + copysign (0.5 , integral )) * coefficient );
66
+ }
105
67
106
68
switch (mode ) {
107
69
case PHP_ROUND_HALF_UP :
108
- if (fractional >= 0.5 ) {
70
+ if (value_abs >= edge_case ) {
109
71
/* We must increase the magnitude of the integral part
110
72
* (rounding up / towards infinity). copysign(1.0, integral)
111
73
* will either result in 1.0 or -1.0 depending on the sign
@@ -120,7 +82,7 @@ static inline double php_round_helper(double value, int mode) {
120
82
return integral ;
121
83
122
84
case PHP_ROUND_HALF_DOWN :
123
- if (fractional > 0.5 ) {
85
+ if (value_abs > edge_case ) {
124
86
return integral + copysign (1.0 , integral );
125
87
}
126
88
@@ -151,11 +113,9 @@ static inline double php_round_helper(double value, int mode) {
151
113
return integral ;
152
114
153
115
case PHP_ROUND_HALF_EVEN :
154
- if (fractional > 0.5 ) {
116
+ if (value_abs > edge_case ) {
155
117
return integral + copysign (1.0 , integral );
156
- }
157
-
158
- if (UNEXPECTED (fractional == 0.5 )) {
118
+ } else if (value_abs == edge_case ) {
159
119
bool even = !fmod (integral , 2.0 );
160
120
161
121
/* If the integral part is not even we can make it even
@@ -169,11 +129,9 @@ static inline double php_round_helper(double value, int mode) {
169
129
return integral ;
170
130
171
131
case PHP_ROUND_HALF_ODD :
172
- if (fractional > 0.5 ) {
132
+ if (value_abs > edge_case ) {
173
133
return integral + copysign (1.0 , integral );
174
- }
175
-
176
- if (UNEXPECTED (fractional == 0.5 )) {
134
+ } else if (value_abs == edge_case ) {
177
135
bool even = !fmod (integral , 2.0 );
178
136
179
137
if (even ) {
@@ -196,56 +154,30 @@ static inline double php_round_helper(double value, int mode) {
196
154
* mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
197
155
*/
198
156
PHPAPI double _php_math_round (double value , int places , int mode ) {
199
- double f1 , f2 ;
157
+ double f1 ;
200
158
double tmp_value ;
201
- int precision_places ;
202
159
203
160
if (!zend_finite (value ) || value == 0.0 ) {
204
161
return value ;
205
162
}
206
163
207
164
places = places < INT_MIN + 1 ? INT_MIN + 1 : places ;
208
- precision_places = 14 - php_intlog10abs (value );
209
165
210
166
f1 = php_intpow10 (abs (places ));
211
167
212
- /* If the decimal precision guaranteed by FP arithmetic is higher than
213
- the requested places BUT is small enough to make sure a non-zero value
214
- is returned, pre-round the result to the precision */
215
- if (precision_places > places && precision_places - 15 < places ) {
216
- int64_t use_precision = precision_places < INT_MIN + 1 ? INT_MIN + 1 : precision_places ;
217
-
218
- f2 = php_intpow10 (abs ((int )use_precision ));
219
- if (use_precision >= 0 ) {
220
- tmp_value = value * f2 ;
221
- } else {
222
- tmp_value = value / f2 ;
223
- }
224
- /* preround the result (tmp_value will always be something * 1e14,
225
- thus never larger than 1e15 here) */
226
- tmp_value = php_round_helper (tmp_value , mode );
227
-
228
- use_precision = places - precision_places ;
229
- use_precision = use_precision < INT_MIN + 1 ? INT_MIN + 1 : use_precision ;
230
- /* now correctly move the decimal point */
231
- f2 = php_intpow10 (abs ((int )use_precision ));
232
- /* because places < precision_places */
233
- tmp_value = tmp_value / f2 ;
168
+ /* adjust the value */
169
+ if (places >= 0 ) {
170
+ tmp_value = value * f1 ;
234
171
} else {
235
- /* adjust the value */
236
- if (places >= 0 ) {
237
- tmp_value = value * f1 ;
238
- } else {
239
- tmp_value = value / f1 ;
240
- }
241
- /* This value is beyond our precision, so rounding it is pointless */
242
- if (fabs (tmp_value ) >= 1e15 ) {
243
- return value ;
244
- }
172
+ tmp_value = value / f1 ;
173
+ }
174
+ /* This value is beyond our precision, so rounding it is pointless */
175
+ if (fabs (tmp_value ) >= 1e15 ) {
176
+ return value ;
245
177
}
246
178
247
179
/* round the temp value */
248
- tmp_value = php_round_helper (tmp_value , mode );
180
+ tmp_value = php_round_helper (tmp_value , value , f1 , mode );
249
181
250
182
/* see if it makes sense to use simple division to round the value */
251
183
if (abs (places ) < 23 ) {
0 commit comments