2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 2.00 of the Zend license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.zend.com/license/2_00.txt. |
12 | If you did not receive a copy of the Zend license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@zend.com so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/zend-math.h"
25 ///////////////////////////////////////////////////////////////////////////////
27 static inline int php_intlog10abs(double value
) {
31 if (value
< 1e-8 || value
> 1e22
) {
32 result
= (int)floor(log10(value
));
34 static const double values
[] = {
35 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
36 1e0
, 1e1
, 1e2
, 1e3
, 1e4
, 1e5
, 1e6
, 1e7
,
37 1e8
, 1e9
, 1e10
, 1e11
, 1e12
, 1e13
, 1e14
, 1e15
,
38 1e16
, 1e17
, 1e18
, 1e19
, 1e20
, 1e21
, 1e22
};
39 /* Do a binary search with 5 steps */
41 if (value
< values
[result
]) {
46 if (value
< values
[result
]) {
51 if (value
< values
[result
]) {
56 if (value
< values
[result
]) {
61 if (value
< values
[result
]) {
70 static inline double php_intpow10(int power
) {
71 static const double powers
[] = {
72 1e0
, 1e1
, 1e2
, 1e3
, 1e4
, 1e5
, 1e6
, 1e7
,
73 1e8
, 1e9
, 1e10
, 1e11
, 1e12
, 1e13
, 1e14
, 1e15
,
74 1e16
, 1e17
, 1e18
, 1e19
, 1e20
, 1e21
, 1e22
};
76 /* Not in lookup table */
77 if (power
< 0 || power
> 22) {
78 return pow(10.0, (double)power
);
83 static inline double php_round_helper(double value
, int mode
) {
87 tmp_value
= floor(value
+ 0.5);
88 if ((mode
== PHP_ROUND_HALF_DOWN
&& value
== (-0.5 + tmp_value
)) ||
89 (mode
== PHP_ROUND_HALF_EVEN
&& value
== (0.5 + 2 * floor(tmp_value
/2.0))) ||
90 (mode
== PHP_ROUND_HALF_ODD
&& value
== (0.5 + 2 * floor(tmp_value
/2.0) - 1.0)))
92 tmp_value
= tmp_value
- 1.0;
95 tmp_value
= ceil(value
- 0.5);
96 if ((mode
== PHP_ROUND_HALF_DOWN
&& value
== (0.5 + tmp_value
)) ||
97 (mode
== PHP_ROUND_HALF_EVEN
&& value
== (-0.5 + 2 * ceil(tmp_value
/2.0))) ||
98 (mode
== PHP_ROUND_HALF_ODD
&& value
== (-0.5 + 2 * ceil(tmp_value
/2.0) + 1.0)))
100 tmp_value
= tmp_value
+ 1.0;
107 double php_math_round(double value
, int places
,
108 int mode
/* = PHP_ROUND_HALF_UP */) {
111 if (std::isinf(value
)) {
115 int precision_places
= 14 - php_intlog10abs(value
);
116 double f1
= php_intpow10(abs(places
));
118 /* If the decimal precision guaranteed by FP arithmetic is higher than
119 * the requested places BUT is small enough to make sure a non-zero value
120 * is returned, pre-round the result to the precision */
121 if (precision_places
> places
&& precision_places
- places
< 15) {
122 double f2
= php_intpow10(abs(precision_places
));
123 if (precision_places
>= 0) {
124 tmp_value
= value
* f2
;
126 tmp_value
= value
/ f2
;
128 /* preround the result (tmp_value will always be something * 1e14,
129 * thus never larger than 1e15 here) */
130 tmp_value
= php_round_helper(tmp_value
, mode
);
131 /* now correctly move the decimal point */
132 f2
= php_intpow10(abs(places
- precision_places
));
133 /* because places < precision_places */
134 tmp_value
= tmp_value
/ f2
;
136 /* adjust the value */
138 tmp_value
= value
* f1
;
140 tmp_value
= value
/ f1
;
142 /* This value is beyond our precision, so rounding it is pointless */
143 if (fabs(tmp_value
) >= 1e15
) {
148 /* round the temp value */
149 tmp_value
= php_round_helper(tmp_value
, mode
);
151 /* see if it makes sense to use simple division to round the value */
152 if (abs(places
) < 23) {
159 /* Simple division can't be used since that will cause wrong results.
160 * Instead, the number is converted to a string and back again using
161 * strtod(). strtod() will return the nearest possible FP value for
164 /* 40 Bytes should be more than enough for this format string. The
165 * float won't be larger than 1e15 anyway. But just in case, use
166 * snprintf() and make sure the buffer is zero-terminated */
168 snprintf(buf
, 39, "%15fe%d", tmp_value
, -places
);
170 tmp_value
= strtod(buf
, nullptr);
172 /* couldn't convert to string and back */
173 if (std::isinf(tmp_value
)) {
182 ///////////////////////////////////////////////////////////////////////////////