1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 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 */
php_intlog10abs(double value)36 static inline int php_intlog10abs(double value) {
37 int result;
38 value = fabs(value);
39
40 if (value < 1e-8 || value > 1e22) {
41 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 result = 15;
50 if (value < values[result]) {
51 result -= 8;
52 } else {
53 result += 8;
54 }
55 if (value < values[result]) {
56 result -= 4;
57 } else {
58 result += 4;
59 }
60 if (value < values[result]) {
61 result -= 2;
62 } else {
63 result += 2;
64 }
65 if (value < values[result]) {
66 result -= 1;
67 } else {
68 result += 1;
69 }
70 if (value < values[result]) {
71 result -= 1;
72 }
73 result -= 8;
74 }
75 return result;
76 }
77 /* }}} */
78
79 /* {{{ php_intpow10
80 Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
php_intpow10(int power)81 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 if (power < 0 || power > 22) {
89 return pow(10.0, (double)power);
90 }
91 return powers[power];
92 }
93 /* }}} */
94
95 /* {{{ php_math_is_finite */
php_math_is_finite(double value)96 static inline int php_math_is_finite(double value) {
97 #if defined(PHP_WIN32)
98 return _finite(value);
99 #elif defined(isfinite)
100 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 */
php_round_helper(double value,int mode)109 static inline double php_round_helper(double value, int mode) {
110 double tmp_value;
111
112 if (value >= 0.0) {
113 tmp_value = floor(value + 0.5);
114 if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
115 (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
116 (mode == PHP_ROUND_HALF_ODD && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
117 {
118 tmp_value = tmp_value - 1.0;
119 }
120 } else {
121 tmp_value = ceil(value - 0.5);
122 if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
123 (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
124 (mode == PHP_ROUND_HALF_ODD && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
125 {
126 tmp_value = tmp_value + 1.0;
127 }
128 }
129
130 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 */
_php_math_round(double value,int places,int mode)139 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 if (!php_math_is_finite(value)) {
145 return value;
146 }
147
148 precision_places = 14 - php_intlog10abs(value);
149
150 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 if (precision_places > places && precision_places - places < 15) {
156 f2 = php_intpow10(abs(precision_places));
157 if (precision_places >= 0) {
158 tmp_value = value * f2;
159 } else {
160 tmp_value = value / f2;
161 }
162 /* preround the result (tmp_value will always be something * 1e14,
163 thus never larger than 1e15 here) */
164 tmp_value = php_round_helper(tmp_value, mode);
165 /* now correctly move the decimal point */
166 f2 = php_intpow10(abs(places - precision_places));
167 /* because places < precision_places */
168 tmp_value = tmp_value / f2;
169 } else {
170 /* adjust the value */
171 if (places >= 0) {
172 tmp_value = value * f1;
173 } else {
174 tmp_value = value / f1;
175 }
176 /* This value is beyond our precision, so rounding it is pointless */
177 if (fabs(tmp_value) >= 1e15) {
178 return value;
179 }
180 }
181
182 /* round the temp value */
183 tmp_value = php_round_helper(tmp_value, mode);
184
185 /* see if it makes sense to use simple division to round the value */
186 if (abs(places) < 23) {
187 if (places > 0) {
188 tmp_value = tmp_value / f1;
189 } else {
190 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 snprintf(buf, 39, "%15fe%d", tmp_value, -places);
203 buf[39] = '\0';
204 tmp_value = zend_strtod(buf, NULL);
205 /* couldn't convert to string and back */
206 if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
207 tmp_value = value;
208 }
209 }
210
211 return tmp_value;
212 }
213 /* }}} */
214
215 /* {{{ php_asinh
216 */
php_asinh(double z)217 static double php_asinh(double z)
218 {
219 #ifdef HAVE_ASINH
220 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 */
php_acosh(double x)229 static double php_acosh(double x)
230 {
231 #ifdef HAVE_ACOSH
232 return(acosh(x));
233 #else
234 return(log(x + sqrt(x * x - 1)));
235 #endif
236 }
237 /* }}} */
238
239 /* {{{ php_atanh
240 */
php_atanh(double z)241 static double php_atanh(double z)
242 {
243 #ifdef HAVE_ATANH
244 return(atanh(z));
245 #else
246 return(0.5 * log((1 + z) / (1 - z)));
247 #endif
248 }
249 /* }}} */
250
251 /* {{{ php_log1p
252 */
php_log1p(double x)253 static double php_log1p(double x)
254 {
255 #ifdef HAVE_LOG1P
256 return(log1p(x));
257 #else
258 return(log(1 + x));
259 #endif
260 }
261 /* }}} */
262
263 /* {{{ php_expm1
264 */
php_expm1(double x)265 static double php_expm1(double x)
266 {
267 #if !defined(PHP_WIN32) && !defined(NETWARE)
268 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 */
PHP_FUNCTION(abs)277 PHP_FUNCTION(abs)
278 {
279 zval **value;
280
281 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
282 return;
283 }
284 convert_scalar_to_number_ex(value);
285
286 if (Z_TYPE_PP(value) == IS_DOUBLE) {
287 RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
288 } else if (Z_TYPE_PP(value) == IS_LONG) {
289 if (Z_LVAL_PP(value) == LONG_MIN) {
290 RETURN_DOUBLE(-(double)LONG_MIN);
291 } else {
292 RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
293 }
294 }
295 RETURN_FALSE;
296 }
297 /* }}} */
298
299 /* {{{ proto float ceil(float number)
300 Returns the next highest integer value of the number */
PHP_FUNCTION(ceil)301 PHP_FUNCTION(ceil)
302 {
303 zval **value;
304
305 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
306 return;
307 }
308 convert_scalar_to_number_ex(value);
309
310 if (Z_TYPE_PP(value) == IS_DOUBLE) {
311 RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
312 } else if (Z_TYPE_PP(value) == IS_LONG) {
313 convert_to_double_ex(value);
314 RETURN_DOUBLE(Z_DVAL_PP(value));
315 }
316 RETURN_FALSE;
317 }
318 /* }}} */
319
320 /* {{{ proto float floor(float number)
321 Returns the next lowest integer value from the number */
PHP_FUNCTION(floor)322 PHP_FUNCTION(floor)
323 {
324 zval **value;
325
326 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
327 return;
328 }
329 convert_scalar_to_number_ex(value);
330
331 if (Z_TYPE_PP(value) == IS_DOUBLE) {
332 RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
333 } else if (Z_TYPE_PP(value) == IS_LONG) {
334 convert_to_double_ex(value);
335 RETURN_DOUBLE(Z_DVAL_PP(value));
336 }
337 RETURN_FALSE;
338 }
339 /* }}} */
340
341 /* {{{ proto float round(float number [, int precision [, int mode]])
342 Returns the number rounded to specified precision */
PHP_FUNCTION(round)343 PHP_FUNCTION(round)
344 {
345 zval **value;
346 int places = 0;
347 long precision = 0;
348 long mode = PHP_ROUND_HALF_UP;
349 double return_val;
350
351 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
352 return;
353 }
354
355 if (ZEND_NUM_ARGS() >= 2) {
356 places = (int) precision;
357 }
358 convert_scalar_to_number_ex(value);
359
360 switch (Z_TYPE_PP(value)) {
361 case IS_LONG:
362 /* Simple case - long that doesn't need to be rounded. */
363 if (places >= 0) {
364 RETURN_DOUBLE((double) Z_LVAL_PP(value));
365 }
366 /* break omitted intentionally */
367
368 case IS_DOUBLE:
369 return_val = (Z_TYPE_PP(value) == IS_LONG) ? (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
370 return_val = _php_math_round(return_val, places, mode);
371 RETURN_DOUBLE(return_val);
372 break;
373
374 default:
375 RETURN_FALSE;
376 break;
377 }
378 }
379 /* }}} */
380
381 /* {{{ proto float sin(float number)
382 Returns the sine of the number in radians */
PHP_FUNCTION(sin)383 PHP_FUNCTION(sin)
384 {
385 double num;
386
387 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
388 return;
389 }
390 RETURN_DOUBLE(sin(num));
391 }
392 /* }}} */
393
394 /* {{{ proto float cos(float number)
395 Returns the cosine of the number in radians */
PHP_FUNCTION(cos)396 PHP_FUNCTION(cos)
397 {
398 double num;
399
400 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
401 return;
402 }
403 RETURN_DOUBLE(cos(num));
404 }
405 /* }}} */
406
407 /* {{{ proto float tan(float number)
408 Returns the tangent of the number in radians */
PHP_FUNCTION(tan)409 PHP_FUNCTION(tan)
410 {
411 double num;
412
413 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
414 return;
415 }
416 RETURN_DOUBLE(tan(num));
417 }
418 /* }}} */
419
420 /* {{{ proto float asin(float number)
421 Returns the arc sine of the number in radians */
PHP_FUNCTION(asin)422 PHP_FUNCTION(asin)
423 {
424 double num;
425
426 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
427 return;
428 }
429 RETURN_DOUBLE(asin(num));
430 }
431 /* }}} */
432
433 /* {{{ proto float acos(float number)
434 Return the arc cosine of the number in radians */
PHP_FUNCTION(acos)435 PHP_FUNCTION(acos)
436 {
437 double num;
438
439 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
440 return;
441 }
442 RETURN_DOUBLE(acos(num));
443 }
444 /* }}} */
445
446 /* {{{ proto float atan(float number)
447 Returns the arc tangent of the number in radians */
PHP_FUNCTION(atan)448 PHP_FUNCTION(atan)
449 {
450 double num;
451
452 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
453 return;
454 }
455 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 */
PHP_FUNCTION(atan2)461 PHP_FUNCTION(atan2)
462 {
463 double num1, num2;
464
465 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
466 return;
467 }
468 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 */
PHP_FUNCTION(sinh)474 PHP_FUNCTION(sinh)
475 {
476 double num;
477
478 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
479 return;
480 }
481 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 */
PHP_FUNCTION(cosh)487 PHP_FUNCTION(cosh)
488 {
489 double num;
490
491 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
492 return;
493 }
494 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) */
PHP_FUNCTION(tanh)500 PHP_FUNCTION(tanh)
501 {
502 double num;
503
504 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
505 return;
506 }
507 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 */
PHP_FUNCTION(asinh)513 PHP_FUNCTION(asinh)
514 {
515 double num;
516
517 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
518 return;
519 }
520 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 */
PHP_FUNCTION(acosh)526 PHP_FUNCTION(acosh)
527 {
528 double num;
529
530 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
531 return;
532 }
533 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 */
PHP_FUNCTION(atanh)539 PHP_FUNCTION(atanh)
540 {
541 double num;
542
543 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
544 return;
545 }
546 RETURN_DOUBLE(php_atanh(num));
547 }
548 /* }}} */
549
550 /* {{{ proto float pi(void)
551 Returns an approximation of pi */
PHP_FUNCTION(pi)552 PHP_FUNCTION(pi)
553 {
554 RETURN_DOUBLE(M_PI);
555 }
556 /* }}} */
557
558 /* {{{ proto bool is_finite(float val)
559 Returns whether argument is finite */
PHP_FUNCTION(is_finite)560 PHP_FUNCTION(is_finite)
561 {
562 double dval;
563
564 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
565 return;
566 }
567 RETURN_BOOL(zend_finite(dval));
568 }
569 /* }}} */
570
571 /* {{{ proto bool is_infinite(float val)
572 Returns whether argument is infinite */
PHP_FUNCTION(is_infinite)573 PHP_FUNCTION(is_infinite)
574 {
575 double dval;
576
577 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
578 return;
579 }
580 RETURN_BOOL(zend_isinf(dval));
581 }
582 /* }}} */
583
584 /* {{{ proto bool is_nan(float val)
585 Returns whether argument is not a number */
PHP_FUNCTION(is_nan)586 PHP_FUNCTION(is_nan)
587 {
588 double dval;
589
590 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
591 return;
592 }
593 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 */
PHP_FUNCTION(pow)599 PHP_FUNCTION(pow)
600 {
601 zval *zbase, *zexp;
602
603 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
604 return;
605 }
606
607 /* make sure we're dealing with numbers */
608 convert_scalar_to_number(zbase TSRMLS_CC);
609 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 if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
613 long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
614
615 if (i == 0) {
616 RETURN_LONG(1L);
617 } else if (l2 == 0) {
618 RETURN_LONG(0);
619 }
620
621 /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
622 while (i >= 1) {
623 int overflow;
624 double dval = 0.0;
625
626 if (i % 2) {
627 --i;
628 ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
629 if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
630 } else {
631 i /= 2;
632 ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
633 if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
634 }
635 if (i == 0) {
636 RETURN_LONG(l1);
637 }
638 }
639 }
640 convert_to_double(zbase);
641 convert_to_double(zexp);
642
643 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 */
PHP_FUNCTION(exp)649 PHP_FUNCTION(exp)
650 {
651 double num;
652
653 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
654 return;
655 }
656
657 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 */
PHP_FUNCTION(expm1)667 PHP_FUNCTION(expm1)
668 {
669 double num;
670
671 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
672 return;
673 }
674 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 */
PHP_FUNCTION(log1p)684 PHP_FUNCTION(log1p)
685 {
686 double num;
687
688 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
689 return;
690 }
691 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 */
PHP_FUNCTION(log)697 PHP_FUNCTION(log)
698 {
699 double num, base = 0;
700
701 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
702 return;
703 }
704 if (ZEND_NUM_ARGS() == 1) {
705 RETURN_DOUBLE(log(num));
706 }
707 if (base <= 0.0) {
708 php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
709 RETURN_FALSE;
710 }
711 if (base == 1) {
712 RETURN_DOUBLE(php_get_nan());
713 } else {
714 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 */
PHP_FUNCTION(log10)721 PHP_FUNCTION(log10)
722 {
723 double num;
724
725 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
726 return;
727 }
728 RETURN_DOUBLE(log10(num));
729 }
730 /* }}} */
731
732 /* {{{ proto float sqrt(float number)
733 Returns the square root of the number */
PHP_FUNCTION(sqrt)734 PHP_FUNCTION(sqrt)
735 {
736 double num;
737
738 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
739 return;
740 }
741 RETURN_DOUBLE(sqrt(num));
742 }
743 /* }}} */
744
745 /* {{{ proto float hypot(float num1, float num2)
746 Returns sqrt(num1*num1 + num2*num2) */
PHP_FUNCTION(hypot)747 PHP_FUNCTION(hypot)
748 {
749 double num1, num2;
750
751 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
752 return;
753 }
754 #if HAVE_HYPOT
755 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 */
PHP_FUNCTION(deg2rad)766 PHP_FUNCTION(deg2rad)
767 {
768 double deg;
769
770 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", °) == FAILURE) {
771 return;
772 }
773 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 */
PHP_FUNCTION(rad2deg)779 PHP_FUNCTION(rad2deg)
780 {
781 double rad;
782
783 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
784 return;
785 }
786 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 */
_php_math_basetolong(zval * arg,int base)794 PHPAPI long _php_math_basetolong(zval *arg, int base)
795 {
796 long num = 0, digit, onum;
797 int i;
798 char c, *s;
799
800 if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
801 return 0;
802 }
803
804 s = Z_STRVAL_P(arg);
805
806 for (i = Z_STRLEN_P(arg); i > 0; i--) {
807 c = *s++;
808
809 digit = (c >= '0' && c <= '9') ? c - '0'
810 : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
811 : (c >= 'a' && c <= 'z') ? c - 'a' + 10
812 : base;
813
814 if (digit >= base) {
815 continue;
816 }
817
818 onum = num;
819 num = num * base + digit;
820 if (num > onum)
821 continue;
822
823 {
824 TSRMLS_FETCH();
825
826 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
827 return LONG_MAX;
828 }
829 }
830
831 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 */
_php_math_basetozval(zval * arg,int base,zval * ret)839 PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
840 {
841 long num = 0;
842 double fnum = 0;
843 int i;
844 int mode = 0;
845 char c, *s;
846 long cutoff;
847 int cutlim;
848
849 if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
850 return FAILURE;
851 }
852
853 s = Z_STRVAL_P(arg);
854
855 cutoff = LONG_MAX / base;
856 cutlim = LONG_MAX % base;
857
858 for (i = Z_STRLEN_P(arg); i > 0; i--) {
859 c = *s++;
860
861 /* might not work for EBCDIC */
862 if (c >= '0' && c <= '9')
863 c -= '0';
864 else if (c >= 'A' && c <= 'Z')
865 c -= 'A' - 10;
866 else if (c >= 'a' && c <= 'z')
867 c -= 'a' - 10;
868 else
869 continue;
870
871 if (c >= base)
872 continue;
873
874 switch (mode) {
875 case 0: /* Integer */
876 if (num < cutoff || (num == cutoff && c <= cutlim)) {
877 num = num * base + c;
878 break;
879 } else {
880 fnum = num;
881 mode = 1;
882 }
883 /* fall-through */
884 case 1: /* Float */
885 fnum = fnum * base + c;
886 }
887 }
888
889 if (mode == 1) {
890 ZVAL_DOUBLE(ret, fnum);
891 } else {
892 ZVAL_LONG(ret, num);
893 }
894 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 */
_php_math_longtobase(zval * arg,int base)903 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 if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
911 return STR_EMPTY_ALLOC();
912 }
913
914 value = Z_LVAL_P(arg);
915
916 end = ptr = buf + sizeof(buf) - 1;
917 *ptr = '\0';
918
919 do {
920 *--ptr = digits[value % base];
921 value /= base;
922 } while (ptr > buf && value);
923
924 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 */
_php_math_zvaltobase(zval * arg,int base TSRMLS_DC)933 PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
934 {
935 static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
936
937 if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
938 return STR_EMPTY_ALLOC();
939 }
940
941 if (Z_TYPE_P(arg) == IS_DOUBLE) {
942 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 if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
948 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
949 return STR_EMPTY_ALLOC();
950 }
951
952 end = ptr = buf + sizeof(buf) - 1;
953 *ptr = '\0';
954
955 do {
956 *--ptr = digits[(int) fmod(fvalue, base)];
957 fvalue /= base;
958 } while (ptr > buf && fabs(fvalue) >= 1);
959
960 return estrndup(ptr, end - ptr);
961 }
962
963 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 */
PHP_FUNCTION(bindec)969 PHP_FUNCTION(bindec)
970 {
971 zval **arg;
972
973 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
974 return;
975 }
976 convert_to_string_ex(arg);
977 if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
978 RETURN_FALSE;
979 }
980 }
981 /* }}} */
982
983 /* {{{ proto int hexdec(string hexadecimal_number)
984 Returns the decimal equivalent of the hexadecimal number */
PHP_FUNCTION(hexdec)985 PHP_FUNCTION(hexdec)
986 {
987 zval **arg;
988
989 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
990 return;
991 }
992 convert_to_string_ex(arg);
993 if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
994 RETURN_FALSE;
995 }
996 }
997 /* }}} */
998
999 /* {{{ proto int octdec(string octal_number)
1000 Returns the decimal equivalent of an octal string */
PHP_FUNCTION(octdec)1001 PHP_FUNCTION(octdec)
1002 {
1003 zval **arg;
1004
1005 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1006 return;
1007 }
1008 convert_to_string_ex(arg);
1009 if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
1010 RETURN_FALSE;
1011 }
1012 }
1013 /* }}} */
1014
1015 /* {{{ proto string decbin(int decimal_number)
1016 Returns a string containing a binary representation of the number */
PHP_FUNCTION(decbin)1017 PHP_FUNCTION(decbin)
1018 {
1019 zval **arg;
1020 char *result;
1021
1022 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1023 return;
1024 }
1025 convert_to_long_ex(arg);
1026 result = _php_math_longtobase(*arg, 2);
1027 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 */
PHP_FUNCTION(decoct)1033 PHP_FUNCTION(decoct)
1034 {
1035 zval **arg;
1036 char *result;
1037
1038 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1039 return;
1040 }
1041 convert_to_long_ex(arg);
1042 result = _php_math_longtobase(*arg, 8);
1043 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 */
PHP_FUNCTION(dechex)1049 PHP_FUNCTION(dechex)
1050 {
1051 zval **arg;
1052 char *result;
1053
1054 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1055 return;
1056 }
1057 convert_to_long_ex(arg);
1058 result = _php_math_longtobase(*arg, 16);
1059 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 */
PHP_FUNCTION(base_convert)1065 PHP_FUNCTION(base_convert)
1066 {
1067 zval **number, temp;
1068 long frombase, tobase;
1069 char *result;
1070
1071 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
1072 return;
1073 }
1074 convert_to_string_ex(number);
1075
1076 if (frombase < 2 || frombase > 36) {
1077 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
1078 RETURN_FALSE;
1079 }
1080 if (tobase < 2 || tobase > 36) {
1081 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
1082 RETURN_FALSE;
1083 }
1084
1085 if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
1086 RETURN_FALSE;
1087 }
1088 result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1089 RETVAL_STRING(result, 0);
1090 }
1091 /* }}} */
1092
1093 /* {{{ _php_math_number_format
1094 */
_php_math_number_format(double d,int dec,char dec_point,char thousand_sep)1095 PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1096 {
1097 char *tmpbuf = NULL, *resbuf;
1098 char *s, *t; /* source, target */
1099 char *dp;
1100 int integral;
1101 int tmplen, reslen=0;
1102 int count=0;
1103 int is_negative=0;
1104
1105 if (d < 0) {
1106 is_negative = 1;
1107 d = -d;
1108 }
1109
1110 dec = MAX(0, dec);
1111 d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1112
1113 tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1114
1115 if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1116 return tmpbuf;
1117 }
1118
1119 /* find decimal point, if expected */
1120 if (dec) {
1121 dp = strpbrk(tmpbuf, ".,");
1122 } else {
1123 dp = NULL;
1124 }
1125
1126 /* calculate the length of the return buffer */
1127 if (dp) {
1128 integral = dp - tmpbuf;
1129 } else {
1130 /* no decimal point was found */
1131 integral = tmplen;
1132 }
1133
1134 /* allow for thousand separators */
1135 if (thousand_sep) {
1136 integral += (integral-1) / 3;
1137 }
1138
1139 reslen = integral;
1140
1141 if (dec) {
1142 reslen += dec;
1143
1144 if (dec_point) {
1145 reslen++;
1146 }
1147 }
1148
1149 /* add a byte for minus sign */
1150 if (is_negative) {
1151 reslen++;
1152 }
1153 resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1154
1155 s = tmpbuf+tmplen-1;
1156 t = resbuf+reslen;
1157 *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 if (dec) {
1163 int declen = dp ? s - dp : 0;
1164 int topad = dec > declen ? dec - declen : 0;
1165
1166 /* pad with '0's */
1167 while (topad--) {
1168 *t-- = '0';
1169 }
1170
1171 if (dp) {
1172 s -= declen + 1; /* +1 to skip the point */
1173 t -= declen;
1174
1175 /* now copy the chars after the point */
1176 memcpy(t + 1, dp + 1, declen);
1177 }
1178
1179 /* add decimal point */
1180 if (dec_point) {
1181 *t-- = dec_point;
1182 }
1183 }
1184
1185 /* copy the numbers before the decimal point, adding thousand
1186 * separator every three digits */
1187 while(s >= tmpbuf) {
1188 *t-- = *s--;
1189 if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1190 *t-- = thousand_sep;
1191 }
1192 }
1193
1194 /* and a minus sign, if needed */
1195 if (is_negative) {
1196 *t-- = '-';
1197 }
1198
1199 efree(tmpbuf);
1200
1201 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 */
PHP_FUNCTION(number_format)1207 PHP_FUNCTION(number_format)
1208 {
1209 double num;
1210 long dec = 0;
1211 char *thousand_sep = NULL, *dec_point = NULL;
1212 char thousand_sep_chr = ',', dec_point_chr = '.';
1213 int thousand_sep_len = 0, dec_point_len = 0;
1214
1215 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 return;
1217 }
1218
1219 switch(ZEND_NUM_ARGS()) {
1220 case 1:
1221 RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
1222 break;
1223 case 2:
1224 RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1225 break;
1226 case 4:
1227 if (dec_point != NULL) {
1228 if (dec_point_len) {
1229 dec_point_chr = dec_point[0];
1230 } else {
1231 dec_point_chr = 0;
1232 }
1233 }
1234 if (thousand_sep != NULL) {
1235 if (thousand_sep_len) {
1236 thousand_sep_chr = thousand_sep[0];
1237 } else {
1238 thousand_sep_chr = 0;
1239 }
1240 }
1241 RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1242 break;
1243 default:
1244 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 */
PHP_FUNCTION(fmod)1252 PHP_FUNCTION(fmod)
1253 {
1254 double num1, num2;
1255
1256 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
1257 return;
1258 }
1259 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 */
1273