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