1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2012 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Authors: Jim Winstead <jimw@php.net> |
16 : | Stig Sæther Bakken <ssb@php.net> |
17 : | Zeev Suraski <zeev@zend.com> |
18 : | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net> |
19 : +----------------------------------------------------------------------+
20 : */
21 :
22 : /* $Id$ */
23 :
24 : #include "php.h"
25 : #include "php_math.h"
26 : #include "zend_multiply.h"
27 :
28 : #include <math.h>
29 : #include <float.h>
30 : #include <stdlib.h>
31 :
32 : #include "basic_functions.h"
33 :
34 : /* {{{ php_intlog10abs
35 : Returns floor(log10(fabs(val))), uses fast binary search */
36 1470 : static inline int php_intlog10abs(double value) {
37 : int result;
38 1470 : value = fabs(value);
39 :
40 1501 : if (value < 1e-8 || value > 1e22) {
41 31 : result = (int)floor(log10(value));
42 : } else {
43 : static const double values[] = {
44 : 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
45 : 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
46 : 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
47 : 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
48 : /* Do a binary search with 5 steps */
49 1439 : result = 15;
50 1439 : if (value < values[result]) {
51 1374 : result -= 8;
52 : } else {
53 65 : result += 8;
54 : }
55 1439 : if (value < values[result]) {
56 69 : result -= 4;
57 : } else {
58 1370 : result += 4;
59 : }
60 1439 : if (value < values[result]) {
61 727 : result -= 2;
62 : } else {
63 712 : result += 2;
64 : }
65 1439 : if (value < values[result]) {
66 724 : result -= 1;
67 : } else {
68 715 : result += 1;
69 : }
70 1439 : if (value < values[result]) {
71 634 : result -= 1;
72 : }
73 1439 : result -= 8;
74 : }
75 1470 : return result;
76 : }
77 : /* }}} */
78 :
79 : /* {{{ php_intpow10
80 : Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
81 4314 : static inline double php_intpow10(int power) {
82 : static const double powers[] = {
83 : 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7,
84 : 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
85 : 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
86 :
87 : /* Not in lookup table */
88 4314 : if (power < 0 || power > 22) {
89 21 : return pow(10.0, (double)power);
90 : }
91 4293 : return powers[power];
92 : }
93 : /* }}} */
94 :
95 : /* {{{ php_math_is_finite */
96 1472 : static inline int php_math_is_finite(double value) {
97 : #if defined(PHP_WIN32)
98 : return _finite(value);
99 : #elif defined(isfinite)
100 1472 : return isfinite(value);
101 : #else
102 : return value == value && (value == 0. || value * 2. != value);
103 : #endif
104 : }
105 : /* }}} */
106 :
107 : /* {{{ php_round_helper
108 : Actually performs the rounding of a value to integer in a certain mode */
109 2865 : static inline double php_round_helper(double value, int mode) {
110 : double tmp_value;
111 :
112 2865 : if (value >= 0.0) {
113 2801 : tmp_value = floor(value + 0.5);
114 2809 : if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
115 4 : (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
116 4 : (mode == PHP_ROUND_HALF_ODD && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
117 : {
118 4 : tmp_value = tmp_value - 1.0;
119 : }
120 : } else {
121 64 : tmp_value = ceil(value - 0.5);
122 72 : if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
123 4 : (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
124 4 : (mode == PHP_ROUND_HALF_ODD && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
125 : {
126 4 : tmp_value = tmp_value + 1.0;
127 : }
128 : }
129 :
130 2865 : return tmp_value;
131 : }
132 : /* }}} */
133 :
134 : /* {{{ _php_math_round */
135 : /*
136 : * Rounds a number to a certain number of decimal places in a certain rounding
137 : * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
138 : */
139 1472 : PHPAPI double _php_math_round(double value, int places, int mode) {
140 : double f1, f2;
141 : double tmp_value;
142 : int precision_places;
143 :
144 1472 : if (!php_math_is_finite(value)) {
145 2 : return value;
146 : }
147 :
148 1470 : precision_places = 14 - php_intlog10abs(value);
149 :
150 1470 : f1 = php_intpow10(abs(places));
151 :
152 : /* If the decimal precision guaranteed by FP arithmetic is higher than
153 : the requested places BUT is small enough to make sure a non-zero value
154 : is returned, pre-round the result to the precision */
155 2892 : if (precision_places > places && precision_places - places < 15) {
156 1422 : f2 = php_intpow10(abs(precision_places));
157 1422 : if (precision_places >= 0) {
158 1416 : tmp_value = value * f2;
159 : } else {
160 6 : tmp_value = value / f2;
161 : }
162 : /* preround the result (tmp_value will always be something * 1e14,
163 : thus never larger than 1e15 here) */
164 1422 : tmp_value = php_round_helper(tmp_value, mode);
165 : /* now correctly move the decimal point */
166 1422 : f2 = php_intpow10(abs(places - precision_places));
167 : /* because places < precision_places */
168 1422 : tmp_value = tmp_value / f2;
169 : } else {
170 : /* adjust the value */
171 48 : if (places >= 0) {
172 46 : tmp_value = value * f1;
173 : } else {
174 2 : tmp_value = value / f1;
175 : }
176 : /* This value is beyond our precision, so rounding it is pointless */
177 48 : if (fabs(tmp_value) >= 1e15) {
178 27 : return value;
179 : }
180 : }
181 :
182 : /* round the temp value */
183 1443 : tmp_value = php_round_helper(tmp_value, mode);
184 :
185 : /* see if it makes sense to use simple division to round the value */
186 2877 : if (abs(places) < 23) {
187 1434 : if (places > 0) {
188 169 : tmp_value = tmp_value / f1;
189 : } else {
190 1265 : tmp_value = tmp_value * f1;
191 : }
192 : } else {
193 : /* Simple division can't be used since that will cause wrong results.
194 : Instead, the number is converted to a string and back again using
195 : strtod(). strtod() will return the nearest possible FP value for
196 : that string. */
197 :
198 : /* 40 Bytes should be more than enough for this format string. The
199 : float won't be larger than 1e15 anyway. But just in case, use
200 : snprintf() and make sure the buffer is zero-terminated */
201 : char buf[40];
202 9 : snprintf(buf, 39, "%15fe%d", tmp_value, -places);
203 9 : buf[39] = '\0';
204 9 : tmp_value = zend_strtod(buf, NULL);
205 : /* couldn't convert to string and back */
206 9 : if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
207 0 : tmp_value = value;
208 : }
209 : }
210 :
211 1443 : return tmp_value;
212 : }
213 : /* }}} */
214 :
215 : /* {{{ php_asinh
216 : */
217 33 : static double php_asinh(double z)
218 : {
219 : #ifdef HAVE_ASINH
220 33 : return(asinh(z));
221 : #else
222 : return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
223 : #endif
224 : }
225 : /* }}} */
226 :
227 : /* {{{ php_acosh
228 : */
229 33 : static double php_acosh(double x)
230 : {
231 : #ifdef HAVE_ACOSH
232 33 : return(acosh(x));
233 : #else
234 : return(log(x + sqrt(x * x - 1)));
235 : #endif
236 : }
237 : /* }}} */
238 :
239 : /* {{{ php_atanh
240 : */
241 33 : static double php_atanh(double z)
242 : {
243 : #ifdef HAVE_ATANH
244 33 : return(atanh(z));
245 : #else
246 : return(0.5 * log((1 + z) / (1 - z)));
247 : #endif
248 : }
249 : /* }}} */
250 :
251 : /* {{{ php_log1p
252 : */
253 46 : static double php_log1p(double x)
254 : {
255 : #ifdef HAVE_LOG1P
256 46 : return(log1p(x));
257 : #else
258 : return(log(1 + x));
259 : #endif
260 : }
261 : /* }}} */
262 :
263 : /* {{{ php_expm1
264 : */
265 45 : static double php_expm1(double x)
266 : {
267 : #if !defined(PHP_WIN32) && !defined(NETWARE)
268 45 : return(expm1(x));
269 : #else
270 : return(exp(x) - 1);
271 : #endif
272 : }
273 : /* }}}*/
274 :
275 : /* {{{ proto int abs(int number)
276 : Return the absolute value of the number */
277 280 : PHP_FUNCTION(abs)
278 : {
279 : zval **value;
280 :
281 280 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
282 2 : return;
283 : }
284 400 : convert_scalar_to_number_ex(value);
285 :
286 278 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
287 211 : RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
288 67 : } else if (Z_TYPE_PP(value) == IS_LONG) {
289 66 : if (Z_LVAL_PP(value) == LONG_MIN) {
290 2 : RETURN_DOUBLE(-(double)LONG_MIN);
291 : } else {
292 64 : RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
293 : }
294 : }
295 1 : RETURN_FALSE;
296 : }
297 : /* }}} */
298 :
299 : /* {{{ proto float ceil(float number)
300 : Returns the next highest integer value of the number */
301 71 : PHP_FUNCTION(ceil)
302 : {
303 : zval **value;
304 :
305 71 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
306 2 : return;
307 : }
308 190 : convert_scalar_to_number_ex(value);
309 :
310 69 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
311 25 : RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
312 44 : } else if (Z_TYPE_PP(value) == IS_LONG) {
313 210 : convert_to_double_ex(value);
314 43 : RETURN_DOUBLE(Z_DVAL_PP(value));
315 : }
316 1 : RETURN_FALSE;
317 : }
318 : /* }}} */
319 :
320 : /* {{{ proto float floor(float number)
321 : Returns the next lowest integer value from the number */
322 435 : PHP_FUNCTION(floor)
323 : {
324 : zval **value;
325 :
326 435 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
327 2 : return;
328 : }
329 554 : convert_scalar_to_number_ex(value);
330 :
331 433 : if (Z_TYPE_PP(value) == IS_DOUBLE) {
332 386 : RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
333 47 : } else if (Z_TYPE_PP(value) == IS_LONG) {
334 222 : convert_to_double_ex(value);
335 46 : RETURN_DOUBLE(Z_DVAL_PP(value));
336 : }
337 1 : RETURN_FALSE;
338 : }
339 : /* }}} */
340 :
341 : /* {{{ proto float round(float number [, int precision [, int mode]])
342 : Returns the number rounded to specified precision */
343 1457 : PHP_FUNCTION(round)
344 : {
345 : zval **value;
346 1457 : int places = 0;
347 1457 : long precision = 0;
348 1457 : long mode = PHP_ROUND_HALF_UP;
349 : double return_val;
350 :
351 1457 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
352 9 : return;
353 : }
354 :
355 1448 : if (ZEND_NUM_ARGS() >= 2) {
356 225 : places = (int) precision;
357 : }
358 1719 : convert_scalar_to_number_ex(value);
359 :
360 1448 : switch (Z_TYPE_PP(value)) {
361 : case IS_LONG:
362 : /* Simple case - long that doesn't need to be rounded. */
363 1292 : if (places >= 0) {
364 1292 : RETURN_DOUBLE((double) Z_LVAL_PP(value));
365 : }
366 : /* break omitted intentionally */
367 :
368 : case IS_DOUBLE:
369 155 : return_val = (Z_TYPE_PP(value) == IS_LONG) ? (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
370 155 : return_val = _php_math_round(return_val, places, mode);
371 155 : RETURN_DOUBLE(return_val);
372 : break;
373 :
374 : default:
375 1 : RETURN_FALSE;
376 : break;
377 : }
378 : }
379 : /* }}} */
380 :
381 : /* {{{ proto float sin(float number)
382 : Returns the sine of the number in radians */
383 45 : PHP_FUNCTION(sin)
384 : {
385 : double num;
386 :
387 45 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
388 3 : return;
389 : }
390 42 : RETURN_DOUBLE(sin(num));
391 : }
392 : /* }}} */
393 :
394 : /* {{{ proto float cos(float number)
395 : Returns the cosine of the number in radians */
396 47 : PHP_FUNCTION(cos)
397 : {
398 : double num;
399 :
400 47 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
401 3 : return;
402 : }
403 44 : RETURN_DOUBLE(cos(num));
404 : }
405 : /* }}} */
406 :
407 : /* {{{ proto float tan(float number)
408 : Returns the tangent of the number in radians */
409 37 : PHP_FUNCTION(tan)
410 : {
411 : double num;
412 :
413 37 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
414 3 : return;
415 : }
416 34 : RETURN_DOUBLE(tan(num));
417 : }
418 : /* }}} */
419 :
420 : /* {{{ proto float asin(float number)
421 : Returns the arc sine of the number in radians */
422 36 : PHP_FUNCTION(asin)
423 : {
424 : double num;
425 :
426 36 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
427 3 : return;
428 : }
429 33 : RETURN_DOUBLE(asin(num));
430 : }
431 : /* }}} */
432 :
433 : /* {{{ proto float acos(float number)
434 : Return the arc cosine of the number in radians */
435 41 : PHP_FUNCTION(acos)
436 : {
437 : double num;
438 :
439 41 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
440 3 : return;
441 : }
442 38 : RETURN_DOUBLE(acos(num));
443 : }
444 : /* }}} */
445 :
446 : /* {{{ proto float atan(float number)
447 : Returns the arc tangent of the number in radians */
448 34 : PHP_FUNCTION(atan)
449 : {
450 : double num;
451 :
452 34 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
453 3 : return;
454 : }
455 31 : RETURN_DOUBLE(atan(num));
456 : }
457 : /* }}} */
458 :
459 : /* {{{ proto float atan2(float y, float x)
460 : Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
461 364 : PHP_FUNCTION(atan2)
462 : {
463 : double num1, num2;
464 :
465 364 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
466 19 : return;
467 : }
468 345 : RETURN_DOUBLE(atan2(num1, num2));
469 : }
470 : /* }}} */
471 :
472 : /* {{{ proto float sinh(float number)
473 : Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
474 40 : PHP_FUNCTION(sinh)
475 : {
476 : double num;
477 :
478 40 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
479 3 : return;
480 : }
481 37 : RETURN_DOUBLE(sinh(num));
482 : }
483 : /* }}} */
484 :
485 : /* {{{ proto float cosh(float number)
486 : Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
487 40 : PHP_FUNCTION(cosh)
488 : {
489 : double num;
490 :
491 40 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
492 3 : return;
493 : }
494 37 : RETURN_DOUBLE(cosh(num));
495 : }
496 : /* }}} */
497 :
498 : /* {{{ proto float tanh(float number)
499 : Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
500 40 : PHP_FUNCTION(tanh)
501 : {
502 : double num;
503 :
504 40 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
505 3 : return;
506 : }
507 37 : RETURN_DOUBLE(tanh(num));
508 : }
509 : /* }}} */
510 :
511 : /* {{{ proto float asinh(float number)
512 : Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
513 36 : PHP_FUNCTION(asinh)
514 : {
515 : double num;
516 :
517 36 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
518 3 : return;
519 : }
520 33 : RETURN_DOUBLE(php_asinh(num));
521 : }
522 : /* }}} */
523 :
524 : /* {{{ proto float acosh(float number)
525 : Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
526 36 : PHP_FUNCTION(acosh)
527 : {
528 : double num;
529 :
530 36 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
531 3 : return;
532 : }
533 33 : RETURN_DOUBLE(php_acosh(num));
534 : }
535 : /* }}} */
536 :
537 : /* {{{ proto float atanh(float number)
538 : Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
539 36 : PHP_FUNCTION(atanh)
540 : {
541 : double num;
542 :
543 36 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
544 3 : return;
545 : }
546 33 : RETURN_DOUBLE(php_atanh(num));
547 : }
548 : /* }}} */
549 :
550 : /* {{{ proto float pi(void)
551 : Returns an approximation of pi */
552 1 : PHP_FUNCTION(pi)
553 : {
554 1 : RETURN_DOUBLE(M_PI);
555 : }
556 : /* }}} */
557 :
558 : /* {{{ proto bool is_finite(float val)
559 : Returns whether argument is finite */
560 58 : PHP_FUNCTION(is_finite)
561 : {
562 : double dval;
563 :
564 58 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
565 10 : return;
566 : }
567 48 : RETURN_BOOL(zend_finite(dval));
568 : }
569 : /* }}} */
570 :
571 : /* {{{ proto bool is_infinite(float val)
572 : Returns whether argument is infinite */
573 66 : PHP_FUNCTION(is_infinite)
574 : {
575 : double dval;
576 :
577 66 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
578 10 : return;
579 : }
580 56 : RETURN_BOOL(zend_isinf(dval));
581 : }
582 : /* }}} */
583 :
584 : /* {{{ proto bool is_nan(float val)
585 : Returns whether argument is not a number */
586 60 : PHP_FUNCTION(is_nan)
587 : {
588 : double dval;
589 :
590 60 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
591 10 : return;
592 : }
593 50 : RETURN_BOOL(zend_isnan(dval));
594 : }
595 : /* }}} */
596 :
597 : /* {{{ proto number pow(number base, number exponent)
598 : Returns base raised to the power of exponent. Returns integer result when possible */
599 2220 : PHP_FUNCTION(pow)
600 : {
601 : zval *zbase, *zexp;
602 :
603 2220 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
604 6 : return;
605 : }
606 :
607 : /* make sure we're dealing with numbers */
608 2214 : convert_scalar_to_number(zbase TSRMLS_CC);
609 2214 : convert_scalar_to_number(zexp TSRMLS_CC);
610 :
611 : /* if both base and exponent were longs, we'll try to get a long out */
612 2214 : if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
613 819 : long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
614 :
615 819 : if (i == 0) {
616 26 : RETURN_LONG(1L);
617 793 : } else if (l2 == 0) {
618 14 : RETURN_LONG(0);
619 : }
620 :
621 : /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
622 5895 : while (i >= 1) {
623 : int overflow;
624 5116 : double dval = 0.0;
625 :
626 5116 : if (i % 2) {
627 1984 : --i;
628 1984 : ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
629 1984 : if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
630 : } else {
631 3132 : i /= 2;
632 3132 : ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
633 3132 : if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
634 : }
635 5026 : if (i == 0) {
636 689 : RETURN_LONG(l1);
637 : }
638 : }
639 : }
640 1395 : convert_to_double(zbase);
641 1395 : convert_to_double(zexp);
642 :
643 1395 : RETURN_DOUBLE(pow(Z_DVAL_P(zbase), Z_DVAL_P(zexp)));
644 : }
645 : /* }}} */
646 :
647 : /* {{{ proto float exp(float number)
648 : Returns e raised to the power of the number */
649 256 : PHP_FUNCTION(exp)
650 : {
651 : double num;
652 :
653 256 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
654 10 : return;
655 : }
656 :
657 246 : RETURN_DOUBLE(exp(num));
658 : }
659 : /* }}} */
660 :
661 : /* {{{ proto float expm1(float number)
662 : Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
663 : /*
664 : WARNING: this function is expermental: it could change its name or
665 : disappear in the next version of PHP!
666 : */
667 59 : PHP_FUNCTION(expm1)
668 : {
669 : double num;
670 :
671 59 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
672 14 : return;
673 : }
674 45 : RETURN_DOUBLE(php_expm1(num));
675 : }
676 : /* }}} */
677 :
678 : /* {{{ proto float log1p(float number)
679 : Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
680 : /*
681 : WARNING: this function is expermental: it could change its name or
682 : disappear in the next version of PHP!
683 : */
684 56 : PHP_FUNCTION(log1p)
685 : {
686 : double num;
687 :
688 56 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
689 10 : return;
690 : }
691 46 : RETURN_DOUBLE(php_log1p(num));
692 : }
693 : /* }}} */
694 :
695 : /* {{{ proto float log(float number, [float base])
696 : Returns the natural logarithm of the number, or the base log if base is specified */
697 746 : PHP_FUNCTION(log)
698 : {
699 746 : double num, base = 0;
700 :
701 746 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
702 18 : return;
703 : }
704 728 : if (ZEND_NUM_ARGS() == 1) {
705 229 : RETURN_DOUBLE(log(num));
706 : }
707 499 : if (base <= 0.0) {
708 10 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
709 10 : RETURN_FALSE;
710 : }
711 489 : if (base == 1) {
712 3 : RETURN_DOUBLE(php_get_nan());
713 : } else {
714 486 : RETURN_DOUBLE(log(num) / log(base));
715 : }
716 : }
717 : /* }}} */
718 :
719 : /* {{{ proto float log10(float number)
720 : Returns the base-10 logarithm of the number */
721 39 : PHP_FUNCTION(log10)
722 : {
723 : double num;
724 :
725 39 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
726 3 : return;
727 : }
728 36 : RETURN_DOUBLE(log10(num));
729 : }
730 : /* }}} */
731 :
732 : /* {{{ proto float sqrt(float number)
733 : Returns the square root of the number */
734 37 : PHP_FUNCTION(sqrt)
735 : {
736 : double num;
737 :
738 37 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
739 3 : return;
740 : }
741 34 : RETURN_DOUBLE(sqrt(num));
742 : }
743 : /* }}} */
744 :
745 : /* {{{ proto float hypot(float num1, float num2)
746 : Returns sqrt(num1*num1 + num2*num2) */
747 389 : PHP_FUNCTION(hypot)
748 : {
749 : double num1, num2;
750 :
751 389 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
752 19 : return;
753 : }
754 : #if HAVE_HYPOT
755 370 : RETURN_DOUBLE(hypot(num1, num2));
756 : #elif defined(_MSC_VER)
757 : RETURN_DOUBLE(_hypot(num1, num2));
758 : #else
759 : RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
760 : #endif
761 : }
762 : /* }}} */
763 :
764 : /* {{{ proto float deg2rad(float number)
765 : Converts the number in degrees to the radian equivalent */
766 39 : PHP_FUNCTION(deg2rad)
767 : {
768 : double deg;
769 :
770 39 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", °) == FAILURE) {
771 3 : return;
772 : }
773 36 : RETURN_DOUBLE((deg / 180.0) * M_PI);
774 : }
775 : /* }}} */
776 :
777 : /* {{{ proto float rad2deg(float number)
778 : Converts the radian number to the equivalent number in degrees */
779 36 : PHP_FUNCTION(rad2deg)
780 : {
781 : double rad;
782 :
783 36 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
784 3 : return;
785 : }
786 33 : RETURN_DOUBLE((rad / M_PI) * 180);
787 : }
788 : /* }}} */
789 :
790 : /* {{{ _php_math_basetolong */
791 : /*
792 : * Convert a string representation of a base(2-36) number to a long.
793 : */
794 0 : PHPAPI long _php_math_basetolong(zval *arg, int base)
795 : {
796 0 : long num = 0, digit, onum;
797 : int i;
798 : char c, *s;
799 :
800 0 : if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
801 0 : return 0;
802 : }
803 :
804 0 : s = Z_STRVAL_P(arg);
805 :
806 0 : for (i = Z_STRLEN_P(arg); i > 0; i--) {
807 0 : c = *s++;
808 :
809 0 : digit = (c >= '0' && c <= '9') ? c - '0'
810 0 : : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
811 0 : : (c >= 'a' && c <= 'z') ? c - 'a' + 10
812 : : base;
813 :
814 0 : if (digit >= base) {
815 0 : continue;
816 : }
817 :
818 0 : onum = num;
819 0 : num = num * base + digit;
820 0 : if (num > onum)
821 0 : continue;
822 :
823 : {
824 : TSRMLS_FETCH();
825 :
826 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
827 0 : return LONG_MAX;
828 : }
829 : }
830 :
831 0 : return num;
832 : }
833 : /* }}} */
834 :
835 : /* {{{ _php_math_basetozval */
836 : /*
837 : * Convert a string representation of a base(2-36) number to a zval.
838 : */
839 438 : PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
840 : {
841 438 : long num = 0;
842 438 : double fnum = 0;
843 : int i;
844 438 : int mode = 0;
845 : char c, *s;
846 : long cutoff;
847 : int cutlim;
848 :
849 438 : if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
850 0 : return FAILURE;
851 : }
852 :
853 438 : s = Z_STRVAL_P(arg);
854 :
855 438 : cutoff = LONG_MAX / base;
856 438 : cutlim = LONG_MAX % base;
857 :
858 2444 : for (i = Z_STRLEN_P(arg); i > 0; i--) {
859 2006 : c = *s++;
860 :
861 : /* might not work for EBCDIC */
862 3580 : if (c >= '0' && c <= '9')
863 1574 : c -= '0';
864 564 : else if (c >= 'A' && c <= 'Z')
865 132 : c -= 'A' - 10;
866 546 : else if (c >= 'a' && c <= 'z')
867 246 : c -= 'a' - 10;
868 : else
869 54 : continue;
870 :
871 1952 : if (c >= base)
872 368 : continue;
873 :
874 1584 : switch (mode) {
875 : case 0: /* Integer */
876 1575 : if (num < cutoff || (num == cutoff && c <= cutlim)) {
877 1566 : num = num * base + c;
878 1566 : break;
879 : } else {
880 9 : fnum = num;
881 9 : mode = 1;
882 : }
883 : /* fall-through */
884 : case 1: /* Float */
885 18 : fnum = fnum * base + c;
886 : }
887 : }
888 :
889 438 : if (mode == 1) {
890 9 : ZVAL_DOUBLE(ret, fnum);
891 : } else {
892 429 : ZVAL_LONG(ret, num);
893 : }
894 438 : return SUCCESS;
895 : }
896 : /* }}} */
897 :
898 : /* {{{ _php_math_longtobase */
899 : /*
900 : * Convert a long to a string containing a base(2-36) representation of
901 : * the number.
902 : */
903 134404 : PHPAPI char * _php_math_longtobase(zval *arg, int base)
904 : {
905 : static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
906 : char buf[(sizeof(unsigned long) << 3) + 1];
907 : char *ptr, *end;
908 : unsigned long value;
909 :
910 134404 : if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
911 0 : return STR_EMPTY_ALLOC();
912 : }
913 :
914 134404 : value = Z_LVAL_P(arg);
915 :
916 134404 : end = ptr = buf + sizeof(buf) - 1;
917 134404 : *ptr = '\0';
918 :
919 : do {
920 548755 : *--ptr = digits[value % base];
921 548755 : value /= base;
922 548755 : } while (ptr > buf && value);
923 :
924 134404 : return estrndup(ptr, end - ptr);
925 : }
926 : /* }}} */
927 :
928 : /* {{{ _php_math_zvaltobase */
929 : /*
930 : * Convert a zval to a string containing a base(2-36) representation of
931 : * the number.
932 : */
933 277 : PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
934 : {
935 : static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
936 :
937 277 : if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
938 0 : return STR_EMPTY_ALLOC();
939 : }
940 :
941 277 : if (Z_TYPE_P(arg) == IS_DOUBLE) {
942 0 : double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
943 : char *ptr, *end;
944 : char buf[(sizeof(double) << 3) + 1];
945 :
946 : /* Don't try to convert +/- infinity */
947 0 : if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
948 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
949 0 : return STR_EMPTY_ALLOC();
950 : }
951 :
952 0 : end = ptr = buf + sizeof(buf) - 1;
953 0 : *ptr = '\0';
954 :
955 : do {
956 0 : *--ptr = digits[(int) fmod(fvalue, base)];
957 0 : fvalue /= base;
958 0 : } while (ptr > buf && fabs(fvalue) >= 1);
959 :
960 0 : return estrndup(ptr, end - ptr);
961 : }
962 :
963 277 : return _php_math_longtobase(arg, base);
964 : }
965 : /* }}} */
966 :
967 : /* {{{ proto int bindec(string binary_number)
968 : Returns the decimal equivalent of the binary number */
969 54 : PHP_FUNCTION(bindec)
970 : {
971 : zval **arg;
972 :
973 54 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
974 2 : return;
975 : }
976 211 : convert_to_string_ex(arg);
977 51 : if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
978 0 : RETURN_FALSE;
979 : }
980 : }
981 : /* }}} */
982 :
983 : /* {{{ proto int hexdec(string hexadecimal_number)
984 : Returns the decimal equivalent of the hexadecimal number */
985 62 : PHP_FUNCTION(hexdec)
986 : {
987 : zval **arg;
988 :
989 62 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
990 2 : return;
991 : }
992 219 : convert_to_string_ex(arg);
993 59 : if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
994 0 : RETURN_FALSE;
995 : }
996 : }
997 : /* }}} */
998 :
999 : /* {{{ proto int octdec(string octal_number)
1000 : Returns the decimal equivalent of an octal string */
1001 54 : PHP_FUNCTION(octdec)
1002 : {
1003 : zval **arg;
1004 :
1005 54 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1006 2 : return;
1007 : }
1008 211 : convert_to_string_ex(arg);
1009 51 : if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
1010 0 : RETURN_FALSE;
1011 : }
1012 : }
1013 : /* }}} */
1014 :
1015 : /* {{{ proto string decbin(int decimal_number)
1016 : Returns a string containing a binary representation of the number */
1017 2581 : PHP_FUNCTION(decbin)
1018 : {
1019 : zval **arg;
1020 : char *result;
1021 :
1022 2581 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1023 0 : return;
1024 : }
1025 2752 : convert_to_long_ex(arg);
1026 2581 : result = _php_math_longtobase(*arg, 2);
1027 2581 : RETURN_STRING(result, 0);
1028 : }
1029 : /* }}} */
1030 :
1031 : /* {{{ proto string decoct(int decimal_number)
1032 : Returns a string containing an octal representation of the given number */
1033 319 : PHP_FUNCTION(decoct)
1034 : {
1035 : zval **arg;
1036 : char *result;
1037 :
1038 319 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1039 2 : return;
1040 : }
1041 488 : convert_to_long_ex(arg);
1042 317 : result = _php_math_longtobase(*arg, 8);
1043 317 : RETURN_STRING(result, 0);
1044 : }
1045 : /* }}} */
1046 :
1047 : /* {{{ proto string dechex(int decimal_number)
1048 : Returns a string containing a hexadecimal representation of the given number */
1049 131231 : PHP_FUNCTION(dechex)
1050 : {
1051 : zval **arg;
1052 : char *result;
1053 :
1054 131231 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1055 2 : return;
1056 : }
1057 131400 : convert_to_long_ex(arg);
1058 131229 : result = _php_math_longtobase(*arg, 16);
1059 131229 : RETURN_STRING(result, 0);
1060 : }
1061 : /* }}} */
1062 :
1063 : /* {{{ proto string base_convert(string number, int frombase, int tobase)
1064 : Converts a number in a string from any base <= 36 to any base <= 36 */
1065 329 : PHP_FUNCTION(base_convert)
1066 : {
1067 : zval **number, temp;
1068 : long frombase, tobase;
1069 : char *result;
1070 :
1071 329 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
1072 15 : return;
1073 : }
1074 1152 : convert_to_string_ex(number);
1075 :
1076 313 : if (frombase < 2 || frombase > 36) {
1077 18 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
1078 18 : RETURN_FALSE;
1079 : }
1080 295 : if (tobase < 2 || tobase > 36) {
1081 18 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
1082 18 : RETURN_FALSE;
1083 : }
1084 :
1085 277 : if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
1086 0 : RETURN_FALSE;
1087 : }
1088 277 : result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1089 277 : RETVAL_STRING(result, 0);
1090 : }
1091 : /* }}} */
1092 :
1093 : /* {{{ _php_math_number_format
1094 : */
1095 1317 : PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1096 : {
1097 1317 : char *tmpbuf = NULL, *resbuf;
1098 : char *s, *t; /* source, target */
1099 : char *dp;
1100 : int integral;
1101 1317 : int tmplen, reslen=0;
1102 1317 : int count=0;
1103 1317 : int is_negative=0;
1104 :
1105 1317 : if (d < 0) {
1106 364 : is_negative = 1;
1107 364 : d = -d;
1108 : }
1109 :
1110 1317 : dec = MAX(0, dec);
1111 1317 : d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1112 :
1113 1317 : tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1114 :
1115 1317 : if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1116 2 : return tmpbuf;
1117 : }
1118 :
1119 : /* find decimal point, if expected */
1120 1315 : if (dec) {
1121 88 : dp = strpbrk(tmpbuf, ".,");
1122 : } else {
1123 1227 : dp = NULL;
1124 : }
1125 :
1126 : /* calculate the length of the return buffer */
1127 1315 : if (dp) {
1128 88 : integral = dp - tmpbuf;
1129 : } else {
1130 : /* no decimal point was found */
1131 1227 : integral = tmplen;
1132 : }
1133 :
1134 : /* allow for thousand separators */
1135 1315 : if (thousand_sep) {
1136 1313 : integral += (integral-1) / 3;
1137 : }
1138 :
1139 1315 : reslen = integral;
1140 :
1141 1315 : if (dec) {
1142 88 : reslen += dec;
1143 :
1144 88 : if (dec_point) {
1145 84 : reslen++;
1146 : }
1147 : }
1148 :
1149 : /* add a byte for minus sign */
1150 1315 : if (is_negative) {
1151 364 : reslen++;
1152 : }
1153 1315 : resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1154 :
1155 1315 : s = tmpbuf+tmplen-1;
1156 1315 : t = resbuf+reslen;
1157 1315 : *t-- = '\0';
1158 :
1159 : /* copy the decimal places.
1160 : * Take care, as the sprintf implementation may return less places than
1161 : * we requested due to internal buffer limitations */
1162 1315 : if (dec) {
1163 88 : int declen = dp ? s - dp : 0;
1164 88 : int topad = dec > declen ? dec - declen : 0;
1165 :
1166 : /* pad with '0's */
1167 4314 : while (topad--) {
1168 4138 : *t-- = '0';
1169 : }
1170 :
1171 88 : if (dp) {
1172 88 : s -= declen + 1; /* +1 to skip the point */
1173 88 : t -= declen;
1174 :
1175 : /* now copy the chars after the point */
1176 88 : memcpy(t + 1, dp + 1, declen);
1177 : }
1178 :
1179 : /* add decimal point */
1180 88 : if (dec_point) {
1181 84 : *t-- = dec_point;
1182 : }
1183 : }
1184 :
1185 : /* copy the numbers before the decimal point, adding thousand
1186 : * separator every three digits */
1187 8323 : while(s >= tmpbuf) {
1188 5693 : *t-- = *s--;
1189 5693 : if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1190 1062 : *t-- = thousand_sep;
1191 : }
1192 : }
1193 :
1194 : /* and a minus sign, if needed */
1195 1315 : if (is_negative) {
1196 364 : *t-- = '-';
1197 : }
1198 :
1199 1315 : efree(tmpbuf);
1200 :
1201 1315 : return resbuf;
1202 : }
1203 : /* }}} */
1204 :
1205 : /* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])
1206 : Formats a number with grouped thousands */
1207 1320 : PHP_FUNCTION(number_format)
1208 : {
1209 : double num;
1210 1320 : long dec = 0;
1211 1320 : char *thousand_sep = NULL, *dec_point = NULL;
1212 1320 : char thousand_sep_chr = ',', dec_point_chr = '.';
1213 1320 : int thousand_sep_len = 0, dec_point_len = 0;
1214 :
1215 1320 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|ls!s!", &num, &dec, &dec_point, &dec_point_len, &thousand_sep, &thousand_sep_len) == FAILURE) {
1216 2 : return;
1217 : }
1218 :
1219 1318 : switch(ZEND_NUM_ARGS()) {
1220 : case 1:
1221 24 : RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
1222 : break;
1223 : case 2:
1224 35 : RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1225 : break;
1226 : case 4:
1227 1258 : if (dec_point != NULL) {
1228 1257 : if (dec_point_len) {
1229 1249 : dec_point_chr = dec_point[0];
1230 : } else {
1231 8 : dec_point_chr = 0;
1232 : }
1233 : }
1234 1258 : if (thousand_sep != NULL) {
1235 1258 : if (thousand_sep_len) {
1236 1256 : thousand_sep_chr = thousand_sep[0];
1237 : } else {
1238 2 : thousand_sep_chr = 0;
1239 : }
1240 : }
1241 1258 : RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1242 : break;
1243 : default:
1244 1 : WRONG_PARAM_COUNT;
1245 : break;
1246 : }
1247 : }
1248 : /* }}} */
1249 :
1250 : /* {{{ proto float fmod(float x, float y)
1251 : Returns the remainder of dividing x by y as a float */
1252 364 : PHP_FUNCTION(fmod)
1253 : {
1254 : double num1, num2;
1255 :
1256 364 : if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
1257 19 : return;
1258 : }
1259 345 : RETURN_DOUBLE(fmod(num1, num2));
1260 : }
1261 : /* }}} */
1262 :
1263 :
1264 :
1265 : /*
1266 : * Local variables:
1267 : * tab-width: 4
1268 : * c-basic-offset: 4
1269 : * End:
1270 : * vim600: fdm=marker
1271 : * vim: noet sw=4 ts=4
1272 : */
|