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