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