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