xref: /PHP-5.3/ext/standard/math.c (revision a2045ff3)
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", &deg) == 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