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