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